aboutsummaryrefslogtreecommitdiff
path: root/CPP/7zip/UI/Common
diff options
context:
space:
mode:
Diffstat (limited to 'CPP/7zip/UI/Common')
-rw-r--r--CPP/7zip/UI/Common/ArchiveCommandLine.cpp1668
-rw-r--r--CPP/7zip/UI/Common/ArchiveCommandLine.h157
-rw-r--r--CPP/7zip/UI/Common/ArchiveExtractCallback.cpp2388
-rw-r--r--CPP/7zip/UI/Common/ArchiveExtractCallback.h536
-rw-r--r--CPP/7zip/UI/Common/ArchiveName.cpp155
-rw-r--r--CPP/7zip/UI/Common/ArchiveName.h10
-rw-r--r--CPP/7zip/UI/Common/ArchiveOpenCallback.cpp166
-rw-r--r--CPP/7zip/UI/Common/ArchiveOpenCallback.h117
-rw-r--r--CPP/7zip/UI/Common/Bench.cpp4583
-rw-r--r--CPP/7zip/UI/Common/Bench.h122
-rw-r--r--CPP/7zip/UI/Common/CompressCall.cpp338
-rw-r--r--CPP/7zip/UI/Common/CompressCall.h28
-rw-r--r--CPP/7zip/UI/Common/CompressCall2.cpp319
-rw-r--r--CPP/7zip/UI/Common/DefaultName.cpp37
-rw-r--r--CPP/7zip/UI/Common/DefaultName.h11
-rw-r--r--CPP/7zip/UI/Common/DirItem.h223
-rw-r--r--CPP/7zip/UI/Common/EnumDirItems.cpp1449
-rw-r--r--CPP/7zip/UI/Common/EnumDirItems.h38
-rw-r--r--CPP/7zip/UI/Common/ExitCode.h27
-rw-r--r--CPP/7zip/UI/Common/Extract.cpp536
-rw-r--r--CPP/7zip/UI/Common/Extract.h103
-rw-r--r--CPP/7zip/UI/Common/ExtractMode.h34
-rw-r--r--CPP/7zip/UI/Common/ExtractingFilePath.cpp287
-rw-r--r--CPP/7zip/UI/Common/ExtractingFilePath.h31
-rw-r--r--CPP/7zip/UI/Common/HashCalc.cpp2045
-rw-r--r--CPP/7zip/UI/Common/HashCalc.h334
-rw-r--r--CPP/7zip/UI/Common/IFileExtractCallback.h114
-rw-r--r--CPP/7zip/UI/Common/LoadCodecs.cpp1321
-rw-r--r--CPP/7zip/UI/Common/LoadCodecs.h474
-rw-r--r--CPP/7zip/UI/Common/OpenArchive.cpp3652
-rw-r--r--CPP/7zip/UI/Common/OpenArchive.h454
-rw-r--r--CPP/7zip/UI/Common/PropIDUtils.cpp696
-rw-r--r--CPP/7zip/UI/Common/PropIDUtils.h18
-rw-r--r--CPP/7zip/UI/Common/Property.h14
-rw-r--r--CPP/7zip/UI/Common/SetProperties.cpp87
-rw-r--r--CPP/7zip/UI/Common/SetProperties.h10
-rw-r--r--CPP/7zip/UI/Common/SortUtils.cpp25
-rw-r--r--CPP/7zip/UI/Common/SortUtils.h10
-rw-r--r--CPP/7zip/UI/Common/StdAfx.h8
-rw-r--r--CPP/7zip/UI/Common/TempFiles.cpp19
-rw-r--r--CPP/7zip/UI/Common/TempFiles.h16
-rw-r--r--CPP/7zip/UI/Common/Update.cpp1763
-rw-r--r--CPP/7zip/UI/Common/Update.h208
-rw-r--r--CPP/7zip/UI/Common/UpdateAction.cpp64
-rw-r--r--CPP/7zip/UI/Common/UpdateAction.h66
-rw-r--r--CPP/7zip/UI/Common/UpdateCallback.cpp842
-rw-r--r--CPP/7zip/UI/Common/UpdateCallback.h165
-rw-r--r--CPP/7zip/UI/Common/UpdatePair.cpp235
-rw-r--r--CPP/7zip/UI/Common/UpdatePair.h27
-rw-r--r--CPP/7zip/UI/Common/UpdateProduce.cpp70
-rw-r--r--CPP/7zip/UI/Common/UpdateProduce.h55
-rw-r--r--CPP/7zip/UI/Common/WorkDir.cpp93
-rw-r--r--CPP/7zip/UI/Common/WorkDir.h26
-rw-r--r--CPP/7zip/UI/Common/ZipRegistry.cpp521
-rw-r--r--CPP/7zip/UI/Common/ZipRegistry.h163
55 files changed, 26958 insertions, 0 deletions
diff --git a/CPP/7zip/UI/Common/ArchiveCommandLine.cpp b/CPP/7zip/UI/Common/ArchiveCommandLine.cpp
new file mode 100644
index 0000000..91ef038
--- /dev/null
+++ b/CPP/7zip/UI/Common/ArchiveCommandLine.cpp
@@ -0,0 +1,1668 @@
1// ArchiveCommandLine.cpp
2
3#include "StdAfx.h"
4#undef printf
5#undef sprintf
6
7#ifdef _WIN32
8#ifndef UNDER_CE
9#include <io.h>
10#endif
11#else
12// for isatty()
13#include <unistd.h>
14#endif
15
16#include <stdio.h>
17
18#ifdef _7ZIP_LARGE_PAGES
19#include "../../../../C/Alloc.h"
20#endif
21
22#include "../../../Common/IntToString.h"
23#include "../../../Common/ListFileUtils.h"
24#include "../../../Common/StringConvert.h"
25#include "../../../Common/StringToInt.h"
26
27#include "../../../Windows/ErrorMsg.h"
28#include "../../../Windows/FileDir.h"
29#include "../../../Windows/FileName.h"
30#include "../../../Windows/System.h"
31#ifdef _WIN32
32#include "../../../Windows/FileMapping.h"
33#include "../../../Windows/MemoryLock.h"
34#include "../../../Windows/Synchronization.h"
35#endif
36
37#include "ArchiveCommandLine.h"
38#include "EnumDirItems.h"
39#include "Update.h"
40#include "UpdateAction.h"
41
42extern bool g_CaseSensitive;
43extern bool g_PathTrailReplaceMode;
44
45#ifdef _7ZIP_LARGE_PAGES
46extern
47bool g_LargePagesMode;
48bool g_LargePagesMode = false;
49#endif
50
51/*
52#ifdef ENV_HAVE_LSTAT
53EXTERN_C_BEGIN
54extern int global_use_lstat;
55EXTERN_C_END
56#endif
57*/
58
59#ifdef UNDER_CE
60
61#define MY_IS_TERMINAL(x) false;
62
63#else
64
65// #define MY_isatty_fileno(x) (isatty(fileno(x)))
66// #define MY_IS_TERMINAL(x) (MY_isatty_fileno(x) != 0);
67static inline bool MY_IS_TERMINAL(FILE *x)
68{
69 return (
70 #if defined(_MSC_VER) && (_MSC_VER >= 1400)
71 _isatty(_fileno(x))
72 #else
73 isatty(fileno(x))
74 #endif
75 != 0);
76}
77
78#endif
79
80using namespace NCommandLineParser;
81using namespace NWindows;
82using namespace NFile;
83
84static bool StringToUInt32(const wchar_t *s, UInt32 &v)
85{
86 if (*s == 0)
87 return false;
88 const wchar_t *end;
89 v = ConvertStringToUInt32(s, &end);
90 return *end == 0;
91}
92
93
94namespace NKey {
95enum Enum
96{
97 kHelp1 = 0,
98 kHelp2,
99 kHelp3,
100
101 kDisableHeaders,
102 kDisablePercents,
103 kShowTime,
104 kLogLevel,
105
106 kOutStream,
107 kErrStream,
108 kPercentStream,
109
110 kYes,
111
112 kShowDialog,
113 kOverwrite,
114
115 kArchiveType,
116 kExcludedArcType,
117
118 kProperty,
119 kOutputDir,
120 kWorkingDir,
121
122 kInclude,
123 kExclude,
124 kArInclude,
125 kArExclude,
126 kNoArName,
127
128 kUpdate,
129 kVolume,
130 kRecursed,
131
132 kAffinity,
133 kSfx,
134 kEmail,
135 kHash,
136 // kHashGenFile,
137 kHashDir,
138
139 kStdIn,
140 kStdOut,
141
142 kLargePages,
143 kListfileCharSet,
144 kConsoleCharSet,
145 kTechMode,
146 kListFields,
147
148 kPreserveATime,
149 kShareForWrite,
150 kStopAfterOpenError,
151 kCaseSensitive,
152 kArcNameMode,
153
154 kUseSlashMark,
155 kDisableWildcardParsing,
156 kElimDup,
157 kFullPathMode,
158
159 kHardLinks,
160 kSymLinks_AllowDangerous,
161 kSymLinks,
162 kNtSecurity,
163
164 kAltStreams,
165 kReplaceColonForAltStream,
166 kWriteToAltStreamIfColon,
167
168 kNameTrailReplace,
169
170 kDeleteAfterCompressing,
171 kSetArcMTime
172
173 #ifndef _NO_CRYPTO
174 , kPassword
175 #endif
176};
177
178}
179
180
181static const wchar_t kRecursedIDChar = 'r';
182static const char * const kRecursedPostCharSet = "0-";
183
184static const char * const k_ArcNameMode_PostCharSet = "sea";
185
186static const char * const k_Stream_PostCharSet = "012";
187
188static inline EArcNameMode ParseArcNameMode(int postCharIndex)
189{
190 switch (postCharIndex)
191 {
192 case 1: return k_ArcNameMode_Exact;
193 case 2: return k_ArcNameMode_Add;
194 default: return k_ArcNameMode_Smart;
195 }
196}
197
198namespace NRecursedPostCharIndex {
199 enum EEnum
200 {
201 kWildcardRecursionOnly = 0,
202 kNoRecursion = 1
203 };
204}
205
206// static const char
207#define kImmediateNameID '!'
208#ifdef _WIN32
209#define kMapNameID '#'
210#endif
211#define kFileListID '@'
212
213static const Byte kSomeCludePostStringMinSize = 2; // at least <@|!><N>ame must be
214static const Byte kSomeCludeAfterRecursedPostStringMinSize = 2; // at least <@|!><N>ame must be
215
216static const char * const kOverwritePostCharSet = "asut";
217
218static const NExtract::NOverwriteMode::EEnum k_OverwriteModes[] =
219{
220 NExtract::NOverwriteMode::kOverwrite,
221 NExtract::NOverwriteMode::kSkip,
222 NExtract::NOverwriteMode::kRename,
223 NExtract::NOverwriteMode::kRenameExisting
224};
225
226
227
228#define SWFRM_3(t, mu, mi) t, mu, mi, NULL
229
230#define SWFRM_1(t) SWFRM_3(t, false, 0)
231#define SWFRM_SIMPLE SWFRM_1(NSwitchType::kSimple)
232#define SWFRM_MINUS SWFRM_1(NSwitchType::kMinus)
233#define SWFRM_STRING SWFRM_1(NSwitchType::kString)
234
235#define SWFRM_STRING_SINGL(mi) SWFRM_3(NSwitchType::kString, false, mi)
236#define SWFRM_STRING_MULT(mi) SWFRM_3(NSwitchType::kString, true, mi)
237
238
239static const CSwitchForm kSwitchForms[] =
240{
241 { "?", SWFRM_SIMPLE },
242 { "h", SWFRM_SIMPLE },
243 { "-help", SWFRM_SIMPLE },
244
245 { "ba", SWFRM_SIMPLE },
246 { "bd", SWFRM_SIMPLE },
247 { "bt", SWFRM_SIMPLE },
248 { "bb", SWFRM_STRING_SINGL(0) },
249
250 { "bso", NSwitchType::kChar, false, 1, k_Stream_PostCharSet },
251 { "bse", NSwitchType::kChar, false, 1, k_Stream_PostCharSet },
252 { "bsp", NSwitchType::kChar, false, 1, k_Stream_PostCharSet },
253
254 { "y", SWFRM_SIMPLE },
255
256 { "ad", SWFRM_SIMPLE },
257 { "ao", NSwitchType::kChar, false, 1, kOverwritePostCharSet},
258
259 { "t", SWFRM_STRING_SINGL(1) },
260 { "stx", SWFRM_STRING_MULT(1) },
261
262 { "m", SWFRM_STRING_MULT(1) },
263 { "o", SWFRM_STRING_SINGL(1) },
264 { "w", SWFRM_STRING },
265
266 { "i", SWFRM_STRING_MULT(kSomeCludePostStringMinSize) },
267 { "x", SWFRM_STRING_MULT(kSomeCludePostStringMinSize) },
268 { "ai", SWFRM_STRING_MULT(kSomeCludePostStringMinSize) },
269 { "ax", SWFRM_STRING_MULT(kSomeCludePostStringMinSize) },
270 { "an", SWFRM_SIMPLE },
271
272 { "u", SWFRM_STRING_MULT(1) },
273 { "v", SWFRM_STRING_MULT(1) },
274 { "r", NSwitchType::kChar, false, 0, kRecursedPostCharSet },
275
276 { "stm", SWFRM_STRING },
277 { "sfx", SWFRM_STRING },
278 { "seml", SWFRM_STRING_SINGL(0) },
279 { "scrc", SWFRM_STRING_MULT(0) },
280 // { "scrf", SWFRM_STRING_SINGL(1) },
281 { "shd", SWFRM_STRING_SINGL(1) },
282
283 { "si", SWFRM_STRING },
284 { "so", SWFRM_SIMPLE },
285
286 { "slp", SWFRM_STRING },
287 { "scs", SWFRM_STRING },
288 { "scc", SWFRM_STRING },
289 { "slt", SWFRM_SIMPLE },
290 { "slf", SWFRM_STRING_SINGL(1) },
291
292 { "ssp", SWFRM_SIMPLE },
293 { "ssw", SWFRM_SIMPLE },
294 { "sse", SWFRM_SIMPLE },
295 { "ssc", SWFRM_MINUS },
296 { "sa", NSwitchType::kChar, false, 1, k_ArcNameMode_PostCharSet },
297
298 { "spm", SWFRM_STRING_SINGL(0) },
299 { "spd", SWFRM_SIMPLE },
300 { "spe", SWFRM_MINUS },
301 { "spf", SWFRM_STRING_SINGL(0) },
302
303 { "snh", SWFRM_MINUS },
304 { "snld", SWFRM_MINUS },
305 { "snl", SWFRM_MINUS },
306 { "sni", SWFRM_SIMPLE },
307
308 { "sns", SWFRM_MINUS },
309 { "snr", SWFRM_SIMPLE },
310 { "snc", SWFRM_SIMPLE },
311
312 { "snt", SWFRM_MINUS },
313
314 { "sdel", SWFRM_SIMPLE },
315 { "stl", SWFRM_SIMPLE }
316
317 #ifndef _NO_CRYPTO
318 , { "p", SWFRM_STRING }
319 #endif
320};
321
322static const char * const kUniversalWildcard = "*";
323static const unsigned kMinNonSwitchWords = 1;
324static const unsigned kCommandIndex = 0;
325
326// static const char * const kUserErrorMessage = "Incorrect command line";
327// static const char * const kCannotFindListFile = "Cannot find listfile";
328static const char * const kIncorrectListFile = "Incorrect item in listfile.\nCheck charset encoding and -scs switch.";
329static const char * const kTerminalOutError = "I won't write compressed data to a terminal";
330static const char * const kSameTerminalError = "I won't write data and program's messages to same stream";
331static const char * const kEmptyFilePath = "Empty file path";
332
333bool CArcCommand::IsFromExtractGroup() const
334{
335 switch (CommandType)
336 {
337 case NCommandType::kTest:
338 case NCommandType::kExtract:
339 case NCommandType::kExtractFull:
340 return true;
341 default:
342 return false;
343 }
344}
345
346NExtract::NPathMode::EEnum CArcCommand::GetPathMode() const
347{
348 switch (CommandType)
349 {
350 case NCommandType::kTest:
351 case NCommandType::kExtractFull:
352 return NExtract::NPathMode::kFullPaths;
353 default:
354 return NExtract::NPathMode::kNoPaths;
355 }
356}
357
358bool CArcCommand::IsFromUpdateGroup() const
359{
360 switch (CommandType)
361 {
362 case NCommandType::kAdd:
363 case NCommandType::kUpdate:
364 case NCommandType::kDelete:
365 case NCommandType::kRename:
366 return true;
367 default:
368 return false;
369 }
370}
371
372static NRecursedType::EEnum GetRecursedTypeFromIndex(int index)
373{
374 switch (index)
375 {
376 case NRecursedPostCharIndex::kWildcardRecursionOnly:
377 return NRecursedType::kWildcardOnlyRecursed;
378 case NRecursedPostCharIndex::kNoRecursion:
379 return NRecursedType::kNonRecursed;
380 default:
381 return NRecursedType::kRecursed;
382 }
383}
384
385static const char *g_Commands = "audtexlbih";
386
387static bool ParseArchiveCommand(const UString &commandString, CArcCommand &command)
388{
389 UString s (commandString);
390 s.MakeLower_Ascii();
391 if (s.Len() == 1)
392 {
393 if (s[0] > 0x7F)
394 return false;
395 int index = FindCharPosInString(g_Commands, (char)s[0]);
396 if (index < 0)
397 return false;
398 command.CommandType = (NCommandType::EEnum)index;
399 return true;
400 }
401 if (s.Len() == 2 && s[0] == 'r' && s[1] == 'n')
402 {
403 command.CommandType = (NCommandType::kRename);
404 return true;
405 }
406 return false;
407}
408
409// ------------------------------------------------------------------
410// filenames functions
411
412struct CNameOption
413{
414 bool Include;
415 bool WildcardMatching;
416 Byte MarkMode;
417 NRecursedType::EEnum RecursedType;
418
419 CNameOption():
420 Include(true),
421 WildcardMatching(true),
422 MarkMode(NWildcard::kMark_FileOrDir),
423 RecursedType(NRecursedType::kNonRecursed)
424 {}
425};
426
427
428static void AddNameToCensor(NWildcard::CCensor &censor,
429 const CNameOption &nop, const UString &name)
430{
431 bool recursed = false;
432
433 switch (nop.RecursedType)
434 {
435 case NRecursedType::kWildcardOnlyRecursed:
436 recursed = DoesNameContainWildcard(name);
437 break;
438 case NRecursedType::kRecursed:
439 recursed = true;
440 break;
441 default:
442 break;
443 }
444
445 NWildcard::CCensorPathProps props;
446 props.Recursive = recursed;
447 props.WildcardMatching = nop.WildcardMatching;
448 props.MarkMode = nop.MarkMode;
449 censor.AddPreItem(nop.Include, name, props);
450}
451
452static void AddRenamePair(CObjectVector<CRenamePair> *renamePairs,
453 const UString &oldName, const UString &newName, NRecursedType::EEnum type,
454 bool wildcardMatching)
455{
456 CRenamePair &pair = renamePairs->AddNew();
457 pair.OldName = oldName;
458 pair.NewName = newName;
459 pair.RecursedType = type;
460 pair.WildcardParsing = wildcardMatching;
461
462 if (!pair.Prepare())
463 {
464 UString val;
465 val += pair.OldName;
466 val.Add_LF();
467 val += pair.NewName;
468 val.Add_LF();
469 if (type == NRecursedType::kRecursed)
470 val += "-r";
471 else if (type == NRecursedType::kWildcardOnlyRecursed)
472 val += "-r0";
473 throw CArcCmdLineException("Unsupported rename command:", val);
474 }
475}
476
477static void AddToCensorFromListFile(
478 CObjectVector<CRenamePair> *renamePairs,
479 NWildcard::CCensor &censor,
480 const CNameOption &nop, LPCWSTR fileName, UInt32 codePage)
481{
482 UStringVector names;
483 /*
484 if (!NFind::DoesFileExist_FollowLink(us2fs(fileName)))
485 throw CArcCmdLineException(kCannotFindListFile, fileName);
486 */
487 DWORD lastError = 0;
488 if (!ReadNamesFromListFile2(us2fs(fileName), names, codePage, lastError))
489 {
490 if (lastError != 0)
491 {
492 UString m;
493 m = "The file operation error for listfile";
494 m.Add_LF();
495 m += NError::MyFormatMessage(lastError);
496 throw CArcCmdLineException(m, fileName);
497 }
498 throw CArcCmdLineException(kIncorrectListFile, fileName);
499 }
500 if (renamePairs)
501 {
502 if ((names.Size() & 1) != 0)
503 throw CArcCmdLineException(kIncorrectListFile, fileName);
504 for (unsigned i = 0; i < names.Size(); i += 2)
505 {
506 // change type !!!!
507 AddRenamePair(renamePairs, names[i], names[i + 1], nop.RecursedType, nop.WildcardMatching);
508 }
509 }
510 else
511 FOR_VECTOR (i, names)
512 AddNameToCensor(censor, nop, names[i]);
513}
514
515static void AddToCensorFromNonSwitchesStrings(
516 CObjectVector<CRenamePair> *renamePairs,
517 unsigned startIndex,
518 NWildcard::CCensor &censor,
519 const UStringVector &nonSwitchStrings,
520 int stopSwitchIndex,
521 const CNameOption &nop,
522 bool thereAreSwitchIncludes, UInt32 codePage)
523{
524 // another default
525 if ((renamePairs || nonSwitchStrings.Size() == startIndex) && !thereAreSwitchIncludes)
526 {
527 /* for rename command: -i switch sets the mask for archive item reading.
528 if (thereAreSwitchIncludes), { we don't use UniversalWildcard. }
529 also for non-rename command: we set UniversalWildcard, only if there are no nonSwitches. */
530 // we use default fileds in (CNameOption) for UniversalWildcard.
531 CNameOption nop2;
532 // recursive mode is not important for UniversalWildcard (*)
533 // nop2.RecursedType = nop.RecursedType; // we don't need it
534 /*
535 nop2.RecursedType = NRecursedType::kNonRecursed;
536 nop2.Include = true;
537 nop2.WildcardMatching = true;
538 nop2.MarkMode = NWildcard::kMark_FileOrDir;
539 */
540 AddNameToCensor(censor, nop2, UString(kUniversalWildcard));
541 }
542
543 int oldIndex = -1;
544
545 if (stopSwitchIndex < 0)
546 stopSwitchIndex = (int)nonSwitchStrings.Size();
547
548 for (unsigned i = startIndex; i < nonSwitchStrings.Size(); i++)
549 {
550 const UString &s = nonSwitchStrings[i];
551 if (s.IsEmpty())
552 throw CArcCmdLineException(kEmptyFilePath);
553 if (i < (unsigned)stopSwitchIndex && s[0] == kFileListID)
554 AddToCensorFromListFile(renamePairs, censor, nop, s.Ptr(1), codePage);
555 else if (renamePairs)
556 {
557 if (oldIndex == -1)
558 oldIndex = (int)i;
559 else
560 {
561 // NRecursedType::EEnum type is used for global wildcard (-i! switches)
562 AddRenamePair(renamePairs, nonSwitchStrings[(unsigned)oldIndex], s, NRecursedType::kNonRecursed, nop.WildcardMatching);
563 // AddRenamePair(renamePairs, nonSwitchStrings[oldIndex], s, type);
564 oldIndex = -1;
565 }
566 }
567 else
568 AddNameToCensor(censor, nop, s);
569 }
570
571 if (oldIndex != -1)
572 {
573 throw CArcCmdLineException("There is no second file name for rename pair:", nonSwitchStrings[(unsigned)oldIndex]);
574 }
575}
576
577#ifdef _WIN32
578
579struct CEventSetEnd
580{
581 UString Name;
582
583 CEventSetEnd(const wchar_t *name): Name(name) {}
584 ~CEventSetEnd()
585 {
586 NSynchronization::CManualResetEvent event;
587 if (event.Open(EVENT_MODIFY_STATE, false, GetSystemString(Name)) == 0)
588 event.Set();
589 }
590};
591
592static const char * const k_IncorrectMapCommand = "Incorrect Map command";
593
594static const char *ParseMapWithPaths(
595 NWildcard::CCensor &censor,
596 const UString &s2,
597 const CNameOption &nop)
598{
599 UString s (s2);
600 int pos = s.Find(L':');
601 if (pos < 0)
602 return k_IncorrectMapCommand;
603 int pos2 = s.Find(L':', (unsigned)(pos + 1));
604 if (pos2 < 0)
605 return k_IncorrectMapCommand;
606
607 CEventSetEnd eventSetEnd((const wchar_t *)s + (unsigned)(pos2 + 1));
608 s.DeleteFrom((unsigned)pos2);
609 UInt32 size;
610 if (!StringToUInt32(s.Ptr((unsigned)(pos + 1)), size)
611 || size < sizeof(wchar_t)
612 || size > ((UInt32)1 << 31)
613 || size % sizeof(wchar_t) != 0)
614 return "Unsupported Map data size";
615
616 s.DeleteFrom((unsigned)pos);
617 CFileMapping map;
618 if (map.Open(FILE_MAP_READ, GetSystemString(s)) != 0)
619 return "Cannot open mapping";
620 LPVOID data = map.Map(FILE_MAP_READ, 0, size);
621 if (!data)
622 return "MapViewOfFile error";
623 CFileUnmapper unmapper(data);
624
625 UString name;
626 const wchar_t *p = (const wchar_t *)data;
627 if (*p != 0) // data format marker
628 return "Unsupported Map data";
629 UInt32 numChars = size / sizeof(wchar_t);
630 for (UInt32 i = 1; i < numChars; i++)
631 {
632 wchar_t c = p[i];
633 if (c == 0)
634 {
635 // MessageBoxW(0, name, L"7-Zip", 0);
636 AddNameToCensor(censor, nop, name);
637 name.Empty();
638 }
639 else
640 name += c;
641 }
642 if (!name.IsEmpty())
643 return "Map data error";
644
645 return NULL;
646}
647
648#endif
649
650static void AddSwitchWildcardsToCensor(
651 NWildcard::CCensor &censor,
652 const UStringVector &strings,
653 const CNameOption &nop,
654 UInt32 codePage)
655{
656 const char *errorMessage = NULL;
657 unsigned i;
658 for (i = 0; i < strings.Size(); i++)
659 {
660 const UString &name = strings[i];
661 unsigned pos = 0;
662
663 if (name.Len() < kSomeCludePostStringMinSize)
664 {
665 errorMessage = "Too short switch";
666 break;
667 }
668
669 if (!nop.Include)
670 {
671 if (name.IsEqualTo_Ascii_NoCase("td"))
672 {
673 censor.ExcludeDirItems = true;
674 continue;
675 }
676 if (name.IsEqualTo_Ascii_NoCase("tf"))
677 {
678 censor.ExcludeFileItems = true;
679 continue;
680 }
681 }
682
683 CNameOption nop2 = nop;
684
685 bool type_WasUsed = false;
686 bool recursed_WasUsed = false;
687 bool matching_WasUsed = false;
688 bool error = false;
689
690 for (;;)
691 {
692 wchar_t c = ::MyCharLower_Ascii(name[pos]);
693 if (c == kRecursedIDChar)
694 {
695 if (recursed_WasUsed)
696 {
697 error = true;
698 break;
699 }
700 recursed_WasUsed = true;
701 pos++;
702 c = name[pos];
703 int index = -1;
704 if (c <= 0x7F)
705 index = FindCharPosInString(kRecursedPostCharSet, (char)c);
706 nop2.RecursedType = GetRecursedTypeFromIndex(index);
707 if (index >= 0)
708 {
709 pos++;
710 continue;
711 }
712 }
713
714 if (c == 'w')
715 {
716 if (matching_WasUsed)
717 {
718 error = true;
719 break;
720 }
721 matching_WasUsed = true;
722 nop2.WildcardMatching = true;
723 pos++;
724 if (name[pos] == '-')
725 {
726 nop2.WildcardMatching = false;
727 pos++;
728 }
729 }
730 else if (c == 'm')
731 {
732 if (type_WasUsed)
733 {
734 error = true;
735 break;
736 }
737 type_WasUsed = true;
738 pos++;
739 nop2.MarkMode = NWildcard::kMark_StrictFile;
740 c = name[pos];
741 if (c == '-')
742 {
743 nop2.MarkMode = NWildcard::kMark_FileOrDir;
744 pos++;
745 }
746 else if (c == '2')
747 {
748 nop2.MarkMode = NWildcard::kMark_StrictFile_IfWildcard;
749 pos++;
750 }
751 }
752 else
753 break;
754 }
755
756 if (error)
757 {
758 errorMessage = "inorrect switch";
759 break;
760 }
761
762 if (name.Len() < pos + kSomeCludeAfterRecursedPostStringMinSize)
763 {
764 errorMessage = "Too short switch";
765 break;
766 }
767
768 const UString tail = name.Ptr(pos + 1);
769
770 const wchar_t c = name[pos];
771
772 if (c == kImmediateNameID)
773 AddNameToCensor(censor, nop2, tail);
774 else if (c == kFileListID)
775 AddToCensorFromListFile(NULL, censor, nop2, tail, codePage);
776 #ifdef _WIN32
777 else if (c == kMapNameID)
778 {
779 errorMessage = ParseMapWithPaths(censor, tail, nop2);
780 if (errorMessage)
781 break;
782 }
783 #endif
784 else
785 {
786 errorMessage = "Incorrect wildcard type marker";
787 break;
788 }
789 }
790
791 if (i != strings.Size())
792 throw CArcCmdLineException(errorMessage, strings[i]);
793}
794
795/*
796static NUpdateArchive::NPairAction::EEnum GetUpdatePairActionType(int i)
797{
798 switch (i)
799 {
800 case NUpdateArchive::NPairAction::kIgnore: return NUpdateArchive::NPairAction::kIgnore;
801 case NUpdateArchive::NPairAction::kCopy: return NUpdateArchive::NPairAction::kCopy;
802 case NUpdateArchive::NPairAction::kCompress: return NUpdateArchive::NPairAction::kCompress;
803 case NUpdateArchive::NPairAction::kCompressAsAnti: return NUpdateArchive::NPairAction::kCompressAsAnti;
804 }
805 throw 98111603;
806}
807*/
808
809static const char * const kUpdatePairStateIDSet = "pqrxyzw";
810static const int kUpdatePairStateNotSupportedActions[] = {2, 2, 1, -1, -1, -1, -1};
811
812static const unsigned kNumUpdatePairActions = 4;
813static const char * const kUpdateIgnoreItselfPostStringID = "-";
814static const wchar_t kUpdateNewArchivePostCharID = '!';
815
816
817static bool ParseUpdateCommandString2(const UString &command,
818 NUpdateArchive::CActionSet &actionSet, UString &postString)
819{
820 for (unsigned i = 0; i < command.Len();)
821 {
822 wchar_t c = MyCharLower_Ascii(command[i]);
823 int statePos = FindCharPosInString(kUpdatePairStateIDSet, (char)c);
824 if (c > 0x7F || statePos < 0)
825 {
826 postString = command.Ptr(i);
827 return true;
828 }
829 i++;
830 if (i >= command.Len())
831 return false;
832 c = command[i];
833 if (c < '0' || c >= (wchar_t)('0' + kNumUpdatePairActions))
834 return false;
835 unsigned actionPos = (unsigned)(c - '0');
836 actionSet.StateActions[(unsigned)statePos] = (NUpdateArchive::NPairAction::EEnum)(actionPos);
837 if (kUpdatePairStateNotSupportedActions[(unsigned)statePos] == (int)actionPos)
838 return false;
839 i++;
840 }
841 postString.Empty();
842 return true;
843}
844
845static void ParseUpdateCommandString(CUpdateOptions &options,
846 const UStringVector &updatePostStrings,
847 const NUpdateArchive::CActionSet &defaultActionSet)
848{
849 const char *errorMessage = "incorrect update switch command";
850 unsigned i;
851 for (i = 0; i < updatePostStrings.Size(); i++)
852 {
853 const UString &updateString = updatePostStrings[i];
854 if (updateString.IsEqualTo(kUpdateIgnoreItselfPostStringID))
855 {
856 if (options.UpdateArchiveItself)
857 {
858 options.UpdateArchiveItself = false;
859 options.Commands.Delete(0);
860 }
861 }
862 else
863 {
864 NUpdateArchive::CActionSet actionSet = defaultActionSet;
865
866 UString postString;
867 if (!ParseUpdateCommandString2(updateString, actionSet, postString))
868 break;
869 if (postString.IsEmpty())
870 {
871 if (options.UpdateArchiveItself)
872 options.Commands[0].ActionSet = actionSet;
873 }
874 else
875 {
876 if (postString[0] != kUpdateNewArchivePostCharID)
877 break;
878 CUpdateArchiveCommand uc;
879 UString archivePath = postString.Ptr(1);
880 if (archivePath.IsEmpty())
881 break;
882 uc.UserArchivePath = archivePath;
883 uc.ActionSet = actionSet;
884 options.Commands.Add(uc);
885 }
886 }
887 }
888 if (i != updatePostStrings.Size())
889 throw CArcCmdLineException(errorMessage, updatePostStrings[i]);
890}
891
892bool ParseComplexSize(const wchar_t *s, UInt64 &result);
893
894static void SetAddCommandOptions(
895 NCommandType::EEnum commandType,
896 const CParser &parser,
897 CUpdateOptions &options)
898{
899 NUpdateArchive::CActionSet defaultActionSet;
900 switch (commandType)
901 {
902 case NCommandType::kAdd:
903 defaultActionSet = NUpdateArchive::k_ActionSet_Add;
904 break;
905 case NCommandType::kDelete:
906 defaultActionSet = NUpdateArchive::k_ActionSet_Delete;
907 break;
908 default:
909 defaultActionSet = NUpdateArchive::k_ActionSet_Update;
910 }
911
912 options.UpdateArchiveItself = true;
913
914 options.Commands.Clear();
915 CUpdateArchiveCommand updateMainCommand;
916 updateMainCommand.ActionSet = defaultActionSet;
917 options.Commands.Add(updateMainCommand);
918 if (parser[NKey::kUpdate].ThereIs)
919 ParseUpdateCommandString(options, parser[NKey::kUpdate].PostStrings,
920 defaultActionSet);
921 if (parser[NKey::kWorkingDir].ThereIs)
922 {
923 const UString &postString = parser[NKey::kWorkingDir].PostStrings[0];
924 if (postString.IsEmpty())
925 NDir::MyGetTempPath(options.WorkingDir);
926 else
927 options.WorkingDir = us2fs(postString);
928 }
929 options.SfxMode = parser[NKey::kSfx].ThereIs;
930 if (options.SfxMode)
931 options.SfxModule = us2fs(parser[NKey::kSfx].PostStrings[0]);
932
933 if (parser[NKey::kVolume].ThereIs)
934 {
935 const UStringVector &sv = parser[NKey::kVolume].PostStrings;
936 FOR_VECTOR (i, sv)
937 {
938 UInt64 size;
939 if (!ParseComplexSize(sv[i], size) || size == 0)
940 throw CArcCmdLineException("Incorrect volume size:", sv[i]);
941 options.VolumesSizes.Add(size);
942 }
943 }
944}
945
946static void SetMethodOptions(const CParser &parser, CObjectVector<CProperty> &properties)
947{
948 if (parser[NKey::kProperty].ThereIs)
949 {
950 FOR_VECTOR (i, parser[NKey::kProperty].PostStrings)
951 {
952 CProperty prop;
953 prop.Name = parser[NKey::kProperty].PostStrings[i];
954 int index = prop.Name.Find(L'=');
955 if (index >= 0)
956 {
957 prop.Value = prop.Name.Ptr((unsigned)(index + 1));
958 prop.Name.DeleteFrom((unsigned)index);
959 }
960 properties.Add(prop);
961 }
962 }
963}
964
965
966static inline void SetStreamMode(const CSwitchResult &sw, unsigned &res)
967{
968 if (sw.ThereIs)
969 res = (unsigned)sw.PostCharIndex;
970}
971
972
973#if defined(_WIN32) && !defined(UNDER_CE)
974static void PrintHex(UString &s, UInt64 v)
975{
976 char temp[32];
977 ConvertUInt64ToHex(v, temp);
978 s += temp;
979}
980#endif
981
982
983void CArcCmdLineParser::Parse1(const UStringVector &commandStrings,
984 CArcCmdLineOptions &options)
985{
986 Parse1Log.Empty();
987 if (!parser.ParseStrings(kSwitchForms, ARRAY_SIZE(kSwitchForms), commandStrings))
988 throw CArcCmdLineException(parser.ErrorMessage, parser.ErrorLine);
989
990 options.IsInTerminal = MY_IS_TERMINAL(stdin);
991 options.IsStdOutTerminal = MY_IS_TERMINAL(stdout);
992 options.IsStdErrTerminal = MY_IS_TERMINAL(stderr);
993
994 options.HelpMode = parser[NKey::kHelp1].ThereIs || parser[NKey::kHelp2].ThereIs || parser[NKey::kHelp3].ThereIs;
995
996 options.StdInMode = parser[NKey::kStdIn].ThereIs;
997 options.StdOutMode = parser[NKey::kStdOut].ThereIs;
998 options.EnableHeaders = !parser[NKey::kDisableHeaders].ThereIs;
999 if (parser[NKey::kListFields].ThereIs)
1000 {
1001 const UString &s = parser[NKey::kListFields].PostStrings[0];
1002 options.ListFields = GetAnsiString(s);
1003 }
1004 options.TechMode = parser[NKey::kTechMode].ThereIs;
1005 options.ShowTime = parser[NKey::kShowTime].ThereIs;
1006
1007 if (parser[NKey::kDisablePercents].ThereIs
1008 || options.StdOutMode
1009 || !options.IsStdOutTerminal)
1010 options.Number_for_Percents = k_OutStream_disabled;
1011
1012 if (options.StdOutMode)
1013 options.Number_for_Out = k_OutStream_disabled;
1014
1015 SetStreamMode(parser[NKey::kOutStream], options.Number_for_Out);
1016 SetStreamMode(parser[NKey::kErrStream], options.Number_for_Errors);
1017 SetStreamMode(parser[NKey::kPercentStream], options.Number_for_Percents);
1018
1019 if (parser[NKey::kLogLevel].ThereIs)
1020 {
1021 const UString &s = parser[NKey::kLogLevel].PostStrings[0];
1022 if (s.IsEmpty())
1023 options.LogLevel = 1;
1024 else
1025 {
1026 UInt32 v;
1027 if (!StringToUInt32(s, v))
1028 throw CArcCmdLineException("Unsupported switch postfix -bb", s);
1029 options.LogLevel = (unsigned)v;
1030 }
1031 }
1032
1033 if (parser[NKey::kCaseSensitive].ThereIs)
1034 {
1035 g_CaseSensitive = !parser[NKey::kCaseSensitive].WithMinus;
1036 options.CaseSensitiveChange = true;
1037 options.CaseSensitive = g_CaseSensitive;
1038 }
1039
1040
1041 #if defined(_WIN32) && !defined(UNDER_CE)
1042 NSecurity::EnablePrivilege_SymLink();
1043 #endif
1044
1045 // options.LargePages = false;
1046
1047 if (parser[NKey::kLargePages].ThereIs)
1048 {
1049 unsigned slp = 0;
1050 const UString &s = parser[NKey::kLargePages].PostStrings[0];
1051 if (s.IsEmpty())
1052 slp = 1;
1053 else if (s != L"-")
1054 {
1055 if (!StringToUInt32(s, slp))
1056 throw CArcCmdLineException("Unsupported switch postfix for -slp", s);
1057 }
1058
1059 #ifdef _7ZIP_LARGE_PAGES
1060 if (slp >
1061 #if defined(_WIN32) && !defined(UNDER_CE)
1062 (unsigned)NSecurity::Get_LargePages_RiskLevel()
1063 #else
1064 0
1065 #endif
1066 )
1067 {
1068 #ifdef _WIN32 // change it !
1069 SetLargePageSize();
1070 #endif
1071 // note: this process also can inherit that Privilege from parent process
1072 g_LargePagesMode =
1073 #if defined(_WIN32) && !defined(UNDER_CE)
1074 NSecurity::EnablePrivilege_LockMemory();
1075 #else
1076 true;
1077 #endif
1078 }
1079 #endif
1080 }
1081
1082
1083 #ifndef UNDER_CE
1084
1085 if (parser[NKey::kAffinity].ThereIs)
1086 {
1087 const UString &s = parser[NKey::kAffinity].PostStrings[0];
1088 if (!s.IsEmpty())
1089 {
1090 AString a;
1091 a.SetFromWStr_if_Ascii(s);
1092 Parse1Log += "Set process affinity mask: ";
1093
1094 #ifdef _WIN32
1095
1096 UInt64 v = 0;
1097 {
1098 const char *end;
1099 v = ConvertHexStringToUInt64(a, &end);
1100 if (*end != 0)
1101 a.Empty();
1102 }
1103 if (a.IsEmpty())
1104 throw CArcCmdLineException("Unsupported switch postfix -stm", s);
1105
1106 {
1107 #ifndef _WIN64
1108 if (v >= ((UInt64)1 << 32))
1109 throw CArcCmdLineException("unsupported value -stm", s);
1110 #endif
1111 {
1112 PrintHex(Parse1Log, v);
1113 if (!SetProcessAffinityMask(GetCurrentProcess(), (DWORD_PTR)v))
1114 {
1115 DWORD lastError = GetLastError();
1116 Parse1Log += " : ERROR : ";
1117 Parse1Log += NError::MyFormatMessage(lastError);
1118 }
1119 }
1120 }
1121
1122 #else // _WIN32
1123
1124 {
1125 Parse1Log += a;
1126 NSystem::CProcessAffinity aff;
1127 aff.CpuZero();
1128 for (unsigned i = 0; i < a.Len(); i++)
1129 {
1130 char c = a[i];
1131 unsigned v;
1132 if (c >= '0' && c <= '9') v = (unsigned)(c - '0');
1133 else if (c >= 'A' && c <= 'F') v = 10 + (unsigned)(c - 'A');
1134 else if (c >= 'a' && c <= 'f') v = 10 + (unsigned)(c - 'a');
1135 else
1136 throw CArcCmdLineException("Unsupported switch postfix -stm", s);
1137 for (unsigned k = 0; k < 4; k++)
1138 {
1139 const unsigned cpu = (a.Len() - 1 - i) * 4 + k;
1140 if (v & ((unsigned)1 << k))
1141 aff.CpuSet(cpu);
1142 }
1143 }
1144
1145 if (!aff.SetProcAffinity())
1146 {
1147 DWORD lastError = GetLastError();
1148 Parse1Log += " : ERROR : ";
1149 Parse1Log += NError::MyFormatMessage(lastError);
1150 }
1151 }
1152 #endif // _WIN32
1153
1154 Parse1Log.Add_LF();
1155 }
1156 }
1157
1158 #endif
1159}
1160
1161
1162
1163struct CCodePagePair
1164{
1165 const char *Name;
1166 UInt32 CodePage;
1167};
1168
1169static const unsigned kNumByteOnlyCodePages = 3;
1170
1171static const CCodePagePair g_CodePagePairs[] =
1172{
1173 { "utf-8", CP_UTF8 },
1174 { "win", CP_ACP },
1175 { "dos", CP_OEMCP },
1176 { "utf-16le", MY__CP_UTF16 },
1177 { "utf-16be", MY__CP_UTF16BE }
1178};
1179
1180static Int32 FindCharset(const NCommandLineParser::CParser &parser, unsigned keyIndex,
1181 bool byteOnlyCodePages, Int32 defaultVal)
1182{
1183 if (!parser[keyIndex].ThereIs)
1184 return defaultVal;
1185
1186 UString name (parser[keyIndex].PostStrings.Back());
1187 UInt32 v;
1188 if (StringToUInt32(name, v))
1189 if (v < ((UInt32)1 << 16))
1190 return (Int32)v;
1191 name.MakeLower_Ascii();
1192 unsigned num = byteOnlyCodePages ? kNumByteOnlyCodePages : ARRAY_SIZE(g_CodePagePairs);
1193 for (unsigned i = 0;; i++)
1194 {
1195 if (i == num) // to disable warnings from different compilers
1196 throw CArcCmdLineException("Unsupported charset:", name);
1197 const CCodePagePair &pair = g_CodePagePairs[i];
1198 if (name.IsEqualTo(pair.Name))
1199 return (Int32)pair.CodePage;
1200 }
1201}
1202
1203
1204static void SetBoolPair(NCommandLineParser::CParser &parser, unsigned switchID, CBoolPair &bp)
1205{
1206 bp.Def = parser[switchID].ThereIs;
1207 if (bp.Def)
1208 bp.Val = !parser[switchID].WithMinus;
1209}
1210
1211void CArcCmdLineParser::Parse2(CArcCmdLineOptions &options)
1212{
1213 const UStringVector &nonSwitchStrings = parser.NonSwitchStrings;
1214 const unsigned numNonSwitchStrings = nonSwitchStrings.Size();
1215 if (numNonSwitchStrings < kMinNonSwitchWords)
1216 throw CArcCmdLineException("The command must be specified");
1217
1218 if (!ParseArchiveCommand(nonSwitchStrings[kCommandIndex], options.Command))
1219 throw CArcCmdLineException("Unsupported command:", nonSwitchStrings[kCommandIndex]);
1220
1221 if (parser[NKey::kHash].ThereIs)
1222 options.HashMethods = parser[NKey::kHash].PostStrings;
1223
1224 /*
1225 if (parser[NKey::kHashGenFile].ThereIs)
1226 {
1227 const UString &s = parser[NKey::kHashGenFile].PostStrings[0];
1228 for (unsigned i = 0 ; i < s.Len();)
1229 {
1230 const wchar_t c = s[i++];
1231 if (!options.HashOptions.ParseFlagCharOption(c, true))
1232 {
1233 if (c != '=')
1234 throw CArcCmdLineException("Unsupported hash mode switch:", s);
1235 options.HashOptions.HashFilePath = s.Ptr(i);
1236 break;
1237 }
1238 }
1239 }
1240 */
1241
1242 if (parser[NKey::kHashDir].ThereIs)
1243 options.ExtractOptions.HashDir = parser[NKey::kHashDir].PostStrings[0];
1244
1245 if (parser[NKey::kElimDup].ThereIs)
1246 {
1247 options.ExtractOptions.ElimDup.Def = true;
1248 options.ExtractOptions.ElimDup.Val = !parser[NKey::kElimDup].WithMinus;
1249 }
1250
1251 NWildcard::ECensorPathMode censorPathMode = NWildcard::k_RelatPath;
1252 bool fullPathMode = parser[NKey::kFullPathMode].ThereIs;
1253 if (fullPathMode)
1254 {
1255 censorPathMode = NWildcard::k_AbsPath;
1256 const UString &s = parser[NKey::kFullPathMode].PostStrings[0];
1257 if (!s.IsEmpty())
1258 {
1259 if (s == L"2")
1260 censorPathMode = NWildcard::k_FullPath;
1261 else
1262 throw CArcCmdLineException("Unsupported -spf:", s);
1263 }
1264 }
1265
1266 if (parser[NKey::kNameTrailReplace].ThereIs)
1267 g_PathTrailReplaceMode = !parser[NKey::kNameTrailReplace].WithMinus;
1268
1269 CNameOption nop;
1270
1271 if (parser[NKey::kRecursed].ThereIs)
1272 nop.RecursedType = GetRecursedTypeFromIndex(parser[NKey::kRecursed].PostCharIndex);
1273
1274 if (parser[NKey::kDisableWildcardParsing].ThereIs)
1275 nop.WildcardMatching = false;
1276
1277 if (parser[NKey::kUseSlashMark].ThereIs)
1278 {
1279 const UString &s = parser[NKey::kUseSlashMark].PostStrings[0];
1280 if (s.IsEmpty())
1281 nop.MarkMode = NWildcard::kMark_StrictFile;
1282 else if (s.IsEqualTo_Ascii_NoCase("-"))
1283 nop.MarkMode = NWildcard::kMark_FileOrDir;
1284 else if (s.IsEqualTo_Ascii_NoCase("2"))
1285 nop.MarkMode = NWildcard::kMark_StrictFile_IfWildcard;
1286 else
1287 throw CArcCmdLineException("Unsupported -spm:", s);
1288 }
1289
1290
1291 options.ConsoleCodePage = FindCharset(parser, NKey::kConsoleCharSet, true, -1);
1292
1293 UInt32 codePage = (UInt32)FindCharset(parser, NKey::kListfileCharSet, false, CP_UTF8);
1294
1295 bool thereAreSwitchIncludes = false;
1296
1297 if (parser[NKey::kInclude].ThereIs)
1298 {
1299 thereAreSwitchIncludes = true;
1300 nop.Include = true;
1301 AddSwitchWildcardsToCensor(options.Censor,
1302 parser[NKey::kInclude].PostStrings, nop, codePage);
1303 }
1304
1305 if (parser[NKey::kExclude].ThereIs)
1306 {
1307 nop.Include = false;
1308 AddSwitchWildcardsToCensor(options.Censor,
1309 parser[NKey::kExclude].PostStrings, nop, codePage);
1310 }
1311
1312 unsigned curCommandIndex = kCommandIndex + 1;
1313 bool thereIsArchiveName = !parser[NKey::kNoArName].ThereIs &&
1314 options.Command.CommandType != NCommandType::kBenchmark &&
1315 options.Command.CommandType != NCommandType::kInfo &&
1316 options.Command.CommandType != NCommandType::kHash;
1317
1318 const bool isExtractGroupCommand = options.Command.IsFromExtractGroup();
1319 const bool isExtractOrList = isExtractGroupCommand || options.Command.CommandType == NCommandType::kList;
1320 const bool isRename = options.Command.CommandType == NCommandType::kRename;
1321
1322 if ((isExtractOrList || isRename) && options.StdInMode)
1323 thereIsArchiveName = false;
1324
1325 if (parser[NKey::kArcNameMode].ThereIs)
1326 options.UpdateOptions.ArcNameMode = ParseArcNameMode(parser[NKey::kArcNameMode].PostCharIndex);
1327
1328 if (thereIsArchiveName)
1329 {
1330 if (curCommandIndex >= numNonSwitchStrings)
1331 throw CArcCmdLineException("Cannot find archive name");
1332 options.ArchiveName = nonSwitchStrings[curCommandIndex++];
1333 if (options.ArchiveName.IsEmpty())
1334 throw CArcCmdLineException("Archive name cannot by empty");
1335 #ifdef _WIN32
1336 // options.ArchiveName.Replace(L'/', WCHAR_PATH_SEPARATOR);
1337 #endif
1338 }
1339
1340 nop.Include = true;
1341 AddToCensorFromNonSwitchesStrings(isRename ? &options.UpdateOptions.RenamePairs : NULL,
1342 curCommandIndex, options.Censor,
1343 nonSwitchStrings, parser.StopSwitchIndex,
1344 nop,
1345 thereAreSwitchIncludes, codePage);
1346
1347 options.YesToAll = parser[NKey::kYes].ThereIs;
1348
1349
1350 #ifndef _NO_CRYPTO
1351 options.PasswordEnabled = parser[NKey::kPassword].ThereIs;
1352 if (options.PasswordEnabled)
1353 options.Password = parser[NKey::kPassword].PostStrings[0];
1354 #endif
1355
1356 options.ShowDialog = parser[NKey::kShowDialog].ThereIs;
1357
1358 if (parser[NKey::kArchiveType].ThereIs)
1359 options.ArcType = parser[NKey::kArchiveType].PostStrings[0];
1360
1361 options.ExcludedArcTypes = parser[NKey::kExcludedArcType].PostStrings;
1362
1363 SetMethodOptions(parser, options.Properties);
1364
1365 if (parser[NKey::kNtSecurity].ThereIs) options.NtSecurity.SetTrueTrue();
1366
1367 SetBoolPair(parser, NKey::kAltStreams, options.AltStreams);
1368 SetBoolPair(parser, NKey::kHardLinks, options.HardLinks);
1369 SetBoolPair(parser, NKey::kSymLinks, options.SymLinks);
1370
1371 CBoolPair symLinks_AllowDangerous;
1372 SetBoolPair(parser, NKey::kSymLinks_AllowDangerous, symLinks_AllowDangerous);
1373
1374
1375 /*
1376 bool supportSymLink = options.SymLinks.Val;
1377
1378 if (!options.SymLinks.Def)
1379 {
1380 if (isExtractOrList)
1381 supportSymLink = true;
1382 else
1383 supportSymLink = false;
1384 }
1385
1386 #ifdef ENV_HAVE_LSTAT
1387 if (supportSymLink)
1388 global_use_lstat = 1;
1389 else
1390 global_use_lstat = 0;
1391 #endif
1392 */
1393
1394
1395 if (isExtractOrList)
1396 {
1397 CExtractOptionsBase &eo = options.ExtractOptions;
1398
1399 eo.ExcludeDirItems = options.Censor.ExcludeDirItems;
1400 eo.ExcludeFileItems = options.Censor.ExcludeFileItems;
1401
1402 {
1403 CExtractNtOptions &nt = eo.NtOptions;
1404 nt.NtSecurity = options.NtSecurity;
1405
1406 nt.AltStreams = options.AltStreams;
1407 if (!options.AltStreams.Def)
1408 nt.AltStreams.Val = true;
1409
1410 nt.HardLinks = options.HardLinks;
1411 if (!options.HardLinks.Def)
1412 nt.HardLinks.Val = true;
1413
1414 nt.SymLinks = options.SymLinks;
1415 if (!options.SymLinks.Def)
1416 nt.SymLinks.Val = true;
1417
1418 nt.SymLinks_AllowDangerous = symLinks_AllowDangerous;
1419
1420 nt.ReplaceColonForAltStream = parser[NKey::kReplaceColonForAltStream].ThereIs;
1421 nt.WriteToAltStreamIfColon = parser[NKey::kWriteToAltStreamIfColon].ThereIs;
1422
1423 if (parser[NKey::kPreserveATime].ThereIs)
1424 nt.PreserveATime = true;
1425 if (parser[NKey::kShareForWrite].ThereIs)
1426 nt.OpenShareForWrite = true;
1427 }
1428
1429 options.Censor.AddPathsToCensor(NWildcard::k_AbsPath);
1430 options.Censor.ExtendExclude();
1431
1432 // are there paths that look as non-relative (!Prefix.IsEmpty())
1433 if (!options.Censor.AllAreRelative())
1434 throw CArcCmdLineException("Cannot use absolute pathnames for this command");
1435
1436 NWildcard::CCensor &arcCensor = options.arcCensor;
1437
1438 CNameOption nopArc;
1439 // nopArc.RecursedType = NRecursedType::kNonRecursed; // default: we don't want recursing for archives, if -r specified
1440 // is it OK, external switches can disable WildcardMatching and MarcMode for arc.
1441 nopArc.WildcardMatching = nop.WildcardMatching;
1442 nopArc.MarkMode = nop.MarkMode;
1443
1444 if (parser[NKey::kArInclude].ThereIs)
1445 {
1446 nopArc.Include = true;
1447 AddSwitchWildcardsToCensor(arcCensor, parser[NKey::kArInclude].PostStrings, nopArc, codePage);
1448 }
1449 if (parser[NKey::kArExclude].ThereIs)
1450 {
1451 nopArc.Include = false;
1452 AddSwitchWildcardsToCensor(arcCensor, parser[NKey::kArExclude].PostStrings, nopArc, codePage);
1453 }
1454
1455 if (thereIsArchiveName)
1456 {
1457 nopArc.Include = true;
1458 AddNameToCensor(arcCensor, nopArc, options.ArchiveName);
1459 }
1460
1461 arcCensor.AddPathsToCensor(NWildcard::k_RelatPath);
1462
1463 #ifdef _WIN32
1464 ConvertToLongNames(arcCensor);
1465 #endif
1466
1467 arcCensor.ExtendExclude();
1468
1469 if (options.StdInMode)
1470 options.ArcName_for_StdInMode = parser[NKey::kStdIn].PostStrings.Front();
1471
1472 if (isExtractGroupCommand)
1473 {
1474 if (options.StdOutMode)
1475 {
1476 if (
1477 options.Number_for_Percents == k_OutStream_stdout
1478 // || options.Number_for_Out == k_OutStream_stdout
1479 // || options.Number_for_Errors == k_OutStream_stdout
1480 ||
1481 (
1482 (options.IsStdOutTerminal && options.IsStdErrTerminal)
1483 &&
1484 (
1485 options.Number_for_Percents != k_OutStream_disabled
1486 // || options.Number_for_Out != k_OutStream_disabled
1487 // || options.Number_for_Errors != k_OutStream_disabled
1488 )
1489 )
1490 )
1491 throw CArcCmdLineException(kSameTerminalError);
1492 }
1493
1494 if (parser[NKey::kOutputDir].ThereIs)
1495 {
1496 eo.OutputDir = us2fs(parser[NKey::kOutputDir].PostStrings[0]);
1497 #ifdef _WIN32
1498 NFile::NName::NormalizeDirSeparators(eo.OutputDir);
1499 #endif
1500 NFile::NName::NormalizeDirPathPrefix(eo.OutputDir);
1501 }
1502
1503 eo.OverwriteMode = NExtract::NOverwriteMode::kAsk;
1504 if (parser[NKey::kOverwrite].ThereIs)
1505 {
1506 eo.OverwriteMode = k_OverwriteModes[(unsigned)parser[NKey::kOverwrite].PostCharIndex];
1507 eo.OverwriteMode_Force = true;
1508 }
1509 else if (options.YesToAll)
1510 {
1511 eo.OverwriteMode = NExtract::NOverwriteMode::kOverwrite;
1512 eo.OverwriteMode_Force = true;
1513 }
1514 }
1515
1516 eo.PathMode = options.Command.GetPathMode();
1517 if (censorPathMode == NWildcard::k_AbsPath)
1518 {
1519 eo.PathMode = NExtract::NPathMode::kAbsPaths;
1520 eo.PathMode_Force = true;
1521 }
1522 else if (censorPathMode == NWildcard::k_FullPath)
1523 {
1524 eo.PathMode = NExtract::NPathMode::kFullPaths;
1525 eo.PathMode_Force = true;
1526 }
1527 }
1528 else if (options.Command.IsFromUpdateGroup())
1529 {
1530 if (parser[NKey::kArInclude].ThereIs)
1531 throw CArcCmdLineException("-ai switch is not supported for this command");
1532
1533 CUpdateOptions &updateOptions = options.UpdateOptions;
1534
1535 SetAddCommandOptions(options.Command.CommandType, parser, updateOptions);
1536
1537 updateOptions.MethodMode.Properties = options.Properties;
1538
1539 if (parser[NKey::kPreserveATime].ThereIs)
1540 updateOptions.PreserveATime = true;
1541 if (parser[NKey::kShareForWrite].ThereIs)
1542 updateOptions.OpenShareForWrite = true;
1543 if (parser[NKey::kStopAfterOpenError].ThereIs)
1544 updateOptions.StopAfterOpenError = true;
1545
1546 updateOptions.PathMode = censorPathMode;
1547
1548 updateOptions.AltStreams = options.AltStreams;
1549 updateOptions.NtSecurity = options.NtSecurity;
1550 updateOptions.HardLinks = options.HardLinks;
1551 updateOptions.SymLinks = options.SymLinks;
1552
1553 updateOptions.EMailMode = parser[NKey::kEmail].ThereIs;
1554 if (updateOptions.EMailMode)
1555 {
1556 updateOptions.EMailAddress = parser[NKey::kEmail].PostStrings.Front();
1557 if (updateOptions.EMailAddress.Len() > 0)
1558 if (updateOptions.EMailAddress[0] == L'.')
1559 {
1560 updateOptions.EMailRemoveAfter = true;
1561 updateOptions.EMailAddress.Delete(0);
1562 }
1563 }
1564
1565 updateOptions.StdOutMode = options.StdOutMode;
1566 updateOptions.StdInMode = options.StdInMode;
1567
1568 updateOptions.DeleteAfterCompressing = parser[NKey::kDeleteAfterCompressing].ThereIs;
1569 updateOptions.SetArcMTime = parser[NKey::kSetArcMTime].ThereIs;
1570
1571 if (updateOptions.StdOutMode && updateOptions.EMailMode)
1572 throw CArcCmdLineException("stdout mode and email mode cannot be combined");
1573
1574 if (updateOptions.StdOutMode)
1575 {
1576 if (options.IsStdOutTerminal)
1577 throw CArcCmdLineException(kTerminalOutError);
1578
1579 if (options.Number_for_Percents == k_OutStream_stdout
1580 || options.Number_for_Out == k_OutStream_stdout
1581 || options.Number_for_Errors == k_OutStream_stdout)
1582 throw CArcCmdLineException(kSameTerminalError);
1583 }
1584
1585 if (updateOptions.StdInMode)
1586 updateOptions.StdInFileName = parser[NKey::kStdIn].PostStrings.Front();
1587
1588 if (options.Command.CommandType == NCommandType::kRename)
1589 if (updateOptions.Commands.Size() != 1)
1590 throw CArcCmdLineException("Only one archive can be created with rename command");
1591 }
1592 else if (options.Command.CommandType == NCommandType::kBenchmark)
1593 {
1594 options.NumIterations = 1;
1595 options.NumIterations_Defined = false;
1596 if (curCommandIndex < numNonSwitchStrings)
1597 {
1598 if (!StringToUInt32(nonSwitchStrings[curCommandIndex], options.NumIterations))
1599 throw CArcCmdLineException("Incorrect number of benchmark iterations", nonSwitchStrings[curCommandIndex]);
1600 curCommandIndex++;
1601 options.NumIterations_Defined = true;
1602 }
1603 }
1604 else if (options.Command.CommandType == NCommandType::kHash)
1605 {
1606 options.Censor.AddPathsToCensor(censorPathMode);
1607 options.Censor.ExtendExclude();
1608
1609 CHashOptions &hashOptions = options.HashOptions;
1610 hashOptions.PathMode = censorPathMode;
1611 hashOptions.Methods = options.HashMethods;
1612 // hashOptions.HashFilePath = options.HashFilePath;
1613 if (parser[NKey::kPreserveATime].ThereIs)
1614 hashOptions.PreserveATime = true;
1615 if (parser[NKey::kShareForWrite].ThereIs)
1616 hashOptions.OpenShareForWrite = true;
1617 hashOptions.StdInMode = options.StdInMode;
1618 hashOptions.AltStreamsMode = options.AltStreams.Val;
1619 hashOptions.SymLinks = options.SymLinks;
1620 }
1621 else if (options.Command.CommandType == NCommandType::kInfo)
1622 {
1623 }
1624 else
1625 throw 20150919;
1626}
1627
1628
1629
1630#ifndef _WIN32
1631
1632static AString g_ModuleDirPrefix;
1633
1634void Set_ModuleDirPrefix_From_ProgArg0(const char *s);
1635void Set_ModuleDirPrefix_From_ProgArg0(const char *s)
1636{
1637 AString a (s);
1638 int sep = a.ReverseFind_PathSepar();
1639 a.DeleteFrom((unsigned)(sep + 1));
1640 g_ModuleDirPrefix = a;
1641}
1642
1643namespace NWindows {
1644namespace NDLL {
1645
1646FString GetModuleDirPrefix();
1647FString GetModuleDirPrefix()
1648{
1649 FString s;
1650
1651 s = fas2fs(g_ModuleDirPrefix);
1652 if (s.IsEmpty())
1653 s = FTEXT(".") FSTRING_PATH_SEPARATOR;
1654 return s;
1655 /*
1656 setenv("_7ZIP_HOME_DIR", "/test/", 0);
1657 const char *home = getenv("_7ZIP_HOME_DIR");
1658 if (home)
1659 s = home;
1660 else
1661 s = FTEXT(".") FSTRING_PATH_SEPARATOR;
1662 return s;
1663 */
1664}
1665
1666}}
1667
1668#endif // ! _WIN32
diff --git a/CPP/7zip/UI/Common/ArchiveCommandLine.h b/CPP/7zip/UI/Common/ArchiveCommandLine.h
new file mode 100644
index 0000000..ff4f28c
--- /dev/null
+++ b/CPP/7zip/UI/Common/ArchiveCommandLine.h
@@ -0,0 +1,157 @@
1// ArchiveCommandLine.h
2
3#ifndef __ARCHIVE_COMMAND_LINE_H
4#define __ARCHIVE_COMMAND_LINE_H
5
6#include "../../../Common/CommandLineParser.h"
7#include "../../../Common/Wildcard.h"
8
9#include "EnumDirItems.h"
10
11#include "Extract.h"
12#include "HashCalc.h"
13#include "Update.h"
14
15typedef CMessagePathException CArcCmdLineException;
16
17namespace NCommandType { enum EEnum
18{
19 kAdd = 0,
20 kUpdate,
21 kDelete,
22 kTest,
23 kExtract,
24 kExtractFull,
25 kList,
26 kBenchmark,
27 kInfo,
28 kHash,
29 kRename
30};}
31
32struct CArcCommand
33{
34 NCommandType::EEnum CommandType;
35
36 bool IsFromExtractGroup() const;
37 bool IsFromUpdateGroup() const;
38 bool IsTestCommand() const { return CommandType == NCommandType::kTest; }
39 NExtract::NPathMode::EEnum GetPathMode() const;
40};
41
42enum
43{
44 k_OutStream_disabled = 0,
45 k_OutStream_stdout = 1,
46 k_OutStream_stderr = 2
47};
48
49struct CArcCmdLineOptions
50{
51 bool HelpMode;
52
53 // bool LargePages;
54 bool CaseSensitiveChange;
55 bool CaseSensitive;
56
57 bool IsInTerminal;
58 bool IsStdOutTerminal;
59 bool IsStdErrTerminal;
60 bool StdInMode;
61 bool StdOutMode;
62 bool EnableHeaders;
63
64 bool YesToAll;
65 bool ShowDialog;
66 bool TechMode;
67 bool ShowTime;
68
69 AString ListFields;
70
71 int ConsoleCodePage;
72
73 NWildcard::CCensor Censor;
74
75 CArcCommand Command;
76 UString ArchiveName;
77
78 #ifndef _NO_CRYPTO
79 bool PasswordEnabled;
80 UString Password;
81 #endif
82
83 UStringVector HashMethods;
84 // UString HashFilePath;
85
86 bool AppendName;
87 // UStringVector ArchivePathsSorted;
88 // UStringVector ArchivePathsFullSorted;
89 NWildcard::CCensor arcCensor;
90 UString ArcName_for_StdInMode;
91
92 CObjectVector<CProperty> Properties;
93
94 CExtractOptionsBase ExtractOptions;
95
96 CBoolPair NtSecurity;
97 CBoolPair AltStreams;
98 CBoolPair HardLinks;
99 CBoolPair SymLinks;
100
101 CUpdateOptions UpdateOptions;
102 CHashOptions HashOptions;
103 UString ArcType;
104 UStringVector ExcludedArcTypes;
105
106 unsigned Number_for_Out;
107 unsigned Number_for_Errors;
108 unsigned Number_for_Percents;
109 unsigned LogLevel;
110
111 // bool IsOutAllowed() const { return Number_for_Out != k_OutStream_disabled; }
112
113 // Benchmark
114 UInt32 NumIterations;
115 bool NumIterations_Defined;
116
117 CArcCmdLineOptions():
118 HelpMode(false),
119 // LargePages(false),
120 CaseSensitiveChange(false),
121 CaseSensitive(false),
122
123 IsInTerminal(false),
124 IsStdOutTerminal(false),
125 IsStdErrTerminal(false),
126
127 StdInMode(false),
128 StdOutMode(false),
129
130 EnableHeaders(false),
131
132 YesToAll(false),
133 ShowDialog(false),
134 TechMode(false),
135 ShowTime(false),
136
137 ConsoleCodePage(-1),
138
139 Number_for_Out(k_OutStream_stdout),
140 Number_for_Errors(k_OutStream_stderr),
141 Number_for_Percents(k_OutStream_stdout),
142
143 LogLevel(0)
144 {
145 };
146};
147
148class CArcCmdLineParser
149{
150 NCommandLineParser::CParser parser;
151public:
152 UString Parse1Log;
153 void Parse1(const UStringVector &commandStrings, CArcCmdLineOptions &options);
154 void Parse2(CArcCmdLineOptions &options);
155};
156
157#endif
diff --git a/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp b/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp
new file mode 100644
index 0000000..73c191e
--- /dev/null
+++ b/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp
@@ -0,0 +1,2388 @@
1// ArchiveExtractCallback.cpp
2
3#include "StdAfx.h"
4
5#undef sprintf
6#undef printf
7
8// #include <stdio.h>
9// #include "../../../../C/CpuTicks.h"
10
11#include "../../../../C/Alloc.h"
12#include "../../../../C/CpuArch.h"
13
14
15#include "../../../Common/ComTry.h"
16#include "../../../Common/IntToString.h"
17#include "../../../Common/StringConvert.h"
18#include "../../../Common/UTFConvert.h"
19#include "../../../Common/Wildcard.h"
20
21#include "../../../Windows/ErrorMsg.h"
22#include "../../../Windows/FileDir.h"
23#include "../../../Windows/FileFind.h"
24#include "../../../Windows/FileName.h"
25#include "../../../Windows/PropVariant.h"
26#include "../../../Windows/PropVariantConv.h"
27
28#if defined(_WIN32) && !defined(UNDER_CE) && !defined(_SFX)
29#define _USE_SECURITY_CODE
30#include "../../../Windows/SecurityUtils.h"
31#endif
32
33#include "../../Common/FilePathAutoRename.h"
34#include "../../Common/StreamUtils.h"
35
36#include "../Common/ExtractingFilePath.h"
37#include "../Common/PropIDUtils.h"
38
39#include "ArchiveExtractCallback.h"
40
41using namespace NWindows;
42using namespace NFile;
43using namespace NDir;
44
45static const char * const kCantAutoRename = "Cannot create file with auto name";
46static const char * const kCantRenameFile = "Cannot rename existing file";
47static const char * const kCantDeleteOutputFile = "Cannot delete output file";
48static const char * const kCantDeleteOutputDir = "Cannot delete output folder";
49static const char * const kCantOpenOutFile = "Cannot open output file";
50static const char * const kCantOpenInFile = "Cannot open input file";
51static const char * const kCantSetFileLen = "Cannot set length for output file";
52#ifdef SUPPORT_LINKS
53static const char * const kCantCreateHardLink = "Cannot create hard link";
54static const char * const kCantCreateSymLink = "Cannot create symbolic link";
55#endif
56
57#ifndef _SFX
58
59STDMETHODIMP COutStreamWithHash::Write(const void *data, UInt32 size, UInt32 *processedSize)
60{
61 HRESULT result = S_OK;
62 if (_stream)
63 result = _stream->Write(data, size, &size);
64 if (_calculate)
65 _hash->Update(data, size);
66 _size += size;
67 if (processedSize)
68 *processedSize = size;
69 return result;
70}
71
72#endif // _SFX
73
74
75#ifdef _USE_SECURITY_CODE
76bool InitLocalPrivileges();
77bool InitLocalPrivileges()
78{
79 NSecurity::CAccessToken token;
80 if (!token.OpenProcessToken(GetCurrentProcess(),
81 TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES))
82 return false;
83
84 TOKEN_PRIVILEGES tp;
85
86 tp.PrivilegeCount = 1;
87 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
88
89 if (!::LookupPrivilegeValue(NULL, SE_SECURITY_NAME, &tp.Privileges[0].Luid))
90 return false;
91 if (!token.AdjustPrivileges(&tp))
92 return false;
93 return (GetLastError() == ERROR_SUCCESS);
94}
95#endif // _USE_SECURITY_CODE
96
97
98#ifdef SUPPORT_LINKS
99
100int CHardLinkNode::Compare(const CHardLinkNode &a) const
101{
102 if (StreamId < a.StreamId) return -1;
103 if (StreamId > a.StreamId) return 1;
104 return MyCompare(INode, a.INode);
105}
106
107static HRESULT Archive_Get_HardLinkNode(IInArchive *archive, UInt32 index, CHardLinkNode &h, bool &defined)
108{
109 h.INode = 0;
110 h.StreamId = (UInt64)(Int64)-1;
111 defined = false;
112 {
113 NCOM::CPropVariant prop;
114 RINOK(archive->GetProperty(index, kpidINode, &prop));
115 if (!ConvertPropVariantToUInt64(prop, h.INode))
116 return S_OK;
117 }
118 {
119 NCOM::CPropVariant prop;
120 RINOK(archive->GetProperty(index, kpidStreamId, &prop));
121 ConvertPropVariantToUInt64(prop, h.StreamId);
122 }
123 defined = true;
124 return S_OK;
125}
126
127
128HRESULT CArchiveExtractCallback::PrepareHardLinks(const CRecordVector<UInt32> *realIndices)
129{
130 _hardLinks.Clear();
131
132 if (!_arc->Ask_INode)
133 return S_OK;
134
135 IInArchive *archive = _arc->Archive;
136 CRecordVector<CHardLinkNode> &hardIDs = _hardLinks.IDs;
137
138 {
139 UInt32 numItems;
140 if (realIndices)
141 numItems = realIndices->Size();
142 else
143 {
144 RINOK(archive->GetNumberOfItems(&numItems));
145 }
146
147 for (UInt32 i = 0; i < numItems; i++)
148 {
149 CHardLinkNode h;
150 bool defined;
151 UInt32 realIndex = realIndices ? (*realIndices)[i] : i;
152
153 RINOK(Archive_Get_HardLinkNode(archive, realIndex, h, defined));
154 if (defined)
155 {
156 bool isAltStream = false;
157 RINOK(Archive_IsItem_AltStream(archive, realIndex, isAltStream));
158 if (!isAltStream)
159 hardIDs.Add(h);
160 }
161 }
162 }
163
164 hardIDs.Sort2();
165
166 {
167 // we keep only items that have 2 or more items
168 unsigned k = 0;
169 unsigned numSame = 1;
170 for (unsigned i = 1; i < hardIDs.Size(); i++)
171 {
172 if (hardIDs[i].Compare(hardIDs[i - 1]) != 0)
173 numSame = 1;
174 else if (++numSame == 2)
175 {
176 if (i - 1 != k)
177 hardIDs[k] = hardIDs[i - 1];
178 k++;
179 }
180 }
181 hardIDs.DeleteFrom(k);
182 }
183
184 _hardLinks.PrepareLinks();
185 return S_OK;
186}
187
188#endif // SUPPORT_LINKS
189
190
191CArchiveExtractCallback::CArchiveExtractCallback():
192 _arc(NULL),
193 WriteCTime(true),
194 WriteATime(true),
195 WriteMTime(true),
196 _multiArchives(false)
197{
198 LocalProgressSpec = new CLocalProgress();
199 _localProgress = LocalProgressSpec;
200
201 #ifdef _USE_SECURITY_CODE
202 _saclEnabled = InitLocalPrivileges();
203 #endif
204}
205
206
207void CArchiveExtractCallback::Init(
208 const CExtractNtOptions &ntOptions,
209 const NWildcard::CCensorNode *wildcardCensor,
210 const CArc *arc,
211 IFolderArchiveExtractCallback *extractCallback2,
212 bool stdOutMode, bool testMode,
213 const FString &directoryPath,
214 const UStringVector &removePathParts, bool removePartsForAltStreams,
215 UInt64 packSize)
216{
217 ClearExtractedDirsInfo();
218 _outFileStream.Release();
219 _bufPtrSeqOutStream.Release();
220
221 #ifdef SUPPORT_LINKS
222 _hardLinks.Clear();
223 #endif
224
225 #ifdef SUPPORT_ALT_STREAMS
226 _renamedFiles.Clear();
227 #endif
228
229 _ntOptions = ntOptions;
230 _wildcardCensor = wildcardCensor;
231
232 _stdOutMode = stdOutMode;
233 _testMode = testMode;
234
235 // _progressTotal = 0;
236 // _progressTotal_Defined = false;
237
238 _packTotal = packSize;
239 _progressTotal = packSize;
240 _progressTotal_Defined = true;
241
242 _extractCallback2 = extractCallback2;
243 _compressProgress.Release();
244 _extractCallback2.QueryInterface(IID_ICompressProgressInfo, &_compressProgress);
245 _extractCallback2.QueryInterface(IID_IArchiveExtractCallbackMessage, &_callbackMessage);
246 _extractCallback2.QueryInterface(IID_IFolderArchiveExtractCallback2, &_folderArchiveExtractCallback2);
247
248 #ifndef _SFX
249
250 _extractCallback2.QueryInterface(IID_IFolderExtractToStreamCallback, &ExtractToStreamCallback);
251 if (ExtractToStreamCallback)
252 {
253 Int32 useStreams = 0;
254 if (ExtractToStreamCallback->UseExtractToStream(&useStreams) != S_OK)
255 useStreams = 0;
256 if (useStreams == 0)
257 ExtractToStreamCallback.Release();
258 }
259
260 #endif
261
262 LocalProgressSpec->Init(extractCallback2, true);
263 LocalProgressSpec->SendProgress = false;
264
265 _removePathParts = removePathParts;
266 _removePartsForAltStreams = removePartsForAltStreams;
267
268 #ifndef _SFX
269 _baseParentFolder = (UInt32)(Int32)-1;
270 _use_baseParentFolder_mode = false;
271 #endif
272
273 _arc = arc;
274 _dirPathPrefix = directoryPath;
275 _dirPathPrefix_Full = directoryPath;
276 #if defined(_WIN32) && !defined(UNDER_CE)
277 if (!NName::IsAltPathPrefix(_dirPathPrefix))
278 #endif
279 {
280 NName::NormalizeDirPathPrefix(_dirPathPrefix);
281 NDir::MyGetFullPathName(directoryPath, _dirPathPrefix_Full);
282 NName::NormalizeDirPathPrefix(_dirPathPrefix_Full);
283 }
284}
285
286
287STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 size)
288{
289 COM_TRY_BEGIN
290 _progressTotal = size;
291 _progressTotal_Defined = true;
292 if (!_multiArchives && _extractCallback2)
293 return _extractCallback2->SetTotal(size);
294 return S_OK;
295 COM_TRY_END
296}
297
298
299static void NormalizeVals(UInt64 &v1, UInt64 &v2)
300{
301 const UInt64 kMax = (UInt64)1 << 31;
302 while (v1 > kMax)
303 {
304 v1 >>= 1;
305 v2 >>= 1;
306 }
307}
308
309
310static UInt64 MyMultDiv64(UInt64 unpCur, UInt64 unpTotal, UInt64 packTotal)
311{
312 NormalizeVals(packTotal, unpTotal);
313 NormalizeVals(unpCur, unpTotal);
314 if (unpTotal == 0)
315 unpTotal = 1;
316 return unpCur * packTotal / unpTotal;
317}
318
319
320STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 *completeValue)
321{
322 COM_TRY_BEGIN
323
324 if (!_extractCallback2)
325 return S_OK;
326
327 UInt64 packCur;
328 if (_multiArchives)
329 {
330 packCur = LocalProgressSpec->InSize;
331 if (completeValue && _progressTotal_Defined)
332 packCur += MyMultDiv64(*completeValue, _progressTotal, _packTotal);
333 completeValue = &packCur;
334 }
335 return _extractCallback2->SetCompleted(completeValue);
336
337 COM_TRY_END
338}
339
340
341STDMETHODIMP CArchiveExtractCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)
342{
343 COM_TRY_BEGIN
344 return _localProgress->SetRatioInfo(inSize, outSize);
345 COM_TRY_END
346}
347
348
349void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPathParts, FString &fullPath)
350{
351 // we use (_item.IsDir) in this function
352
353 bool isAbsPath = false;
354
355 if (!dirPathParts.IsEmpty())
356 {
357 const UString &s = dirPathParts[0];
358 if (s.IsEmpty())
359 isAbsPath = true;
360 #if defined(_WIN32) && !defined(UNDER_CE)
361 else
362 {
363 if (NName::IsDrivePath2(s))
364 isAbsPath = true;
365 }
366 #endif
367 }
368
369 if (_pathMode == NExtract::NPathMode::kAbsPaths && isAbsPath)
370 fullPath.Empty();
371 else
372 fullPath = _dirPathPrefix;
373
374 FOR_VECTOR (i, dirPathParts)
375 {
376 if (i != 0)
377 fullPath.Add_PathSepar();
378 const UString &s = dirPathParts[i];
379 fullPath += us2fs(s);
380
381 const bool isFinalDir = (i == dirPathParts.Size() - 1 && _item.IsDir);
382
383 if (fullPath.IsEmpty())
384 {
385 if (isFinalDir)
386 _itemFailure = true;
387 continue;
388 }
389
390 #if defined(_WIN32) && !defined(UNDER_CE)
391 if (_pathMode == NExtract::NPathMode::kAbsPaths)
392 if (i == 0 && s.Len() == 2 && NName::IsDrivePath2(s))
393 {
394 if (isFinalDir)
395 {
396 // we don't want to call SetAttrib() for root drive path
397 _itemFailure = true;
398 }
399 continue;
400 }
401 #endif
402
403 // bool res =
404 CreateDir(fullPath);
405 // if (!res)
406 if (isFinalDir)
407 {
408 if (!NFile::NFind::DoesDirExist(fullPath))
409 {
410 _itemFailure = true;
411 SendMessageError("Cannot create folder", fullPath);
412 // SendMessageError_with_LastError()
413 }
414 }
415 }
416}
417
418
419HRESULT CArchiveExtractCallback::GetTime(UInt32 index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined)
420{
421 filetimeIsDefined = false;
422 filetime.dwLowDateTime = 0;
423 filetime.dwHighDateTime = 0;
424 NCOM::CPropVariant prop;
425 RINOK(_arc->Archive->GetProperty(index, propID, &prop));
426 if (prop.vt == VT_FILETIME)
427 {
428 filetime = prop.filetime;
429 filetimeIsDefined = (filetime.dwHighDateTime != 0 || filetime.dwLowDateTime != 0);
430 }
431 else if (prop.vt != VT_EMPTY)
432 return E_FAIL;
433 return S_OK;
434}
435
436HRESULT CArchiveExtractCallback::GetUnpackSize()
437{
438 return _arc->GetItemSize(_index, _curSize, _curSizeDefined);
439}
440
441static void AddPathToMessage(UString &s, const FString &path)
442{
443 s += " : ";
444 s += fs2us(path);
445}
446
447HRESULT CArchiveExtractCallback::SendMessageError(const char *message, const FString &path)
448{
449 UString s (message);
450 AddPathToMessage(s, path);
451 return _extractCallback2->MessageError(s);
452}
453
454HRESULT CArchiveExtractCallback::SendMessageError_with_LastError(const char *message, const FString &path)
455{
456 DWORD errorCode = GetLastError();
457 UString s (message);
458 if (errorCode != 0)
459 {
460 s += " : ";
461 s += NError::MyFormatMessage(errorCode);
462 }
463 AddPathToMessage(s, path);
464 return _extractCallback2->MessageError(s);
465}
466
467HRESULT CArchiveExtractCallback::SendMessageError2(HRESULT errorCode, const char *message, const FString &path1, const FString &path2)
468{
469 UString s (message);
470 if (errorCode != 0)
471 {
472 s += " : ";
473 s += NError::MyFormatMessage(errorCode);
474 }
475 AddPathToMessage(s, path1);
476 AddPathToMessage(s, path2);
477 return _extractCallback2->MessageError(s);
478}
479
480#ifndef _SFX
481
482STDMETHODIMP CGetProp::GetProp(PROPID propID, PROPVARIANT *value)
483{
484 /*
485 if (propID == kpidName)
486 {
487 COM_TRY_BEGIN
488 NCOM::CPropVariant prop = Name;
489 prop.Detach(value);
490 return S_OK;
491 COM_TRY_END
492 }
493 */
494 return Arc->Archive->GetProperty(IndexInArc, propID, value);
495}
496
497#endif // _SFX
498
499
500#ifdef SUPPORT_LINKS
501
502static UString GetDirPrefixOf(const UString &src)
503{
504 UString s (src);
505 if (!s.IsEmpty())
506 {
507 if (IsPathSepar(s.Back()))
508 s.DeleteBack();
509 int pos = s.ReverseFind_PathSepar();
510 s.DeleteFrom((unsigned)(pos + 1));
511 }
512 return s;
513}
514
515#endif // SUPPORT_LINKS
516
517struct CLinkLevelsInfo
518{
519 bool IsAbsolute;
520 int LowLevel;
521 int FinalLevel;
522
523 void Parse(const UString &path);
524};
525
526void CLinkLevelsInfo::Parse(const UString &path)
527{
528 IsAbsolute = NName::IsAbsolutePath(path);
529
530 LowLevel = 0;
531 FinalLevel = 0;
532
533 UStringVector parts;
534 SplitPathToParts(path, parts);
535 int level = 0;
536
537 FOR_VECTOR (i, parts)
538 {
539 const UString &s = parts[i];
540 if (s.IsEmpty())
541 {
542 if (i == 0)
543 IsAbsolute = true;
544 continue;
545 }
546 if (s == L".")
547 continue;
548 if (s == L"..")
549 {
550 level--;
551 if (LowLevel > level)
552 LowLevel = level;
553 }
554 else
555 level++;
556 }
557
558 FinalLevel = level;
559}
560
561
562bool IsSafePath(const UString &path);
563bool IsSafePath(const UString &path)
564{
565 CLinkLevelsInfo levelsInfo;
566 levelsInfo.Parse(path);
567 return !levelsInfo.IsAbsolute
568 && levelsInfo.LowLevel >= 0
569 && levelsInfo.FinalLevel > 0;
570}
571
572
573bool CensorNode_CheckPath2(const NWildcard::CCensorNode &node, const CReadArcItem &item, bool &include);
574bool CensorNode_CheckPath2(const NWildcard::CCensorNode &node, const CReadArcItem &item, bool &include)
575{
576 bool found = false;
577
578 // CheckPathVect() doesn't check path to Parent nodes
579 if (node.CheckPathVect(item.PathParts, !item.MainIsDir, include))
580 {
581 if (!include)
582 return true;
583
584 #ifdef SUPPORT_ALT_STREAMS
585 if (!item.IsAltStream)
586 return true;
587 #endif
588
589 found = true;
590 }
591
592 #ifdef SUPPORT_ALT_STREAMS
593
594 if (!item.IsAltStream)
595 return false;
596
597 UStringVector pathParts2 = item.PathParts;
598 if (pathParts2.IsEmpty())
599 pathParts2.AddNew();
600 UString &back = pathParts2.Back();
601 back += ':';
602 back += item.AltStreamName;
603 bool include2;
604
605 if (node.CheckPathVect(pathParts2,
606 true, // isFile,
607 include2))
608 {
609 include = include2;
610 return true;
611 }
612
613 #endif // SUPPORT_ALT_STREAMS
614
615 return found;
616}
617
618
619bool CensorNode_CheckPath(const NWildcard::CCensorNode &node, const CReadArcItem &item)
620{
621 bool include;
622 if (CensorNode_CheckPath2(node, item, include))
623 return include;
624 return false;
625}
626
627
628static FString MakePath_from_2_Parts(const FString &prefix, const FString &path)
629{
630 FString s (prefix);
631 #if defined(_WIN32) && !defined(UNDER_CE)
632 if (!path.IsEmpty() && path[0] == ':' && !prefix.IsEmpty() && IsPathSepar(prefix.Back()))
633 {
634 if (!NName::IsDriveRootPath_SuperAllowed(prefix))
635 s.DeleteBack();
636 }
637 #endif
638 s += path;
639 return s;
640}
641
642
643
644#ifdef SUPPORT_LINKS
645
646/*
647struct CTempMidBuffer
648{
649 void *Buf;
650
651 CTempMidBuffer(size_t size): Buf(NULL) { Buf = ::MidAlloc(size); }
652 ~CTempMidBuffer() { ::MidFree(Buf); }
653};
654
655HRESULT CArchiveExtractCallback::MyCopyFile(ISequentialOutStream *outStream)
656{
657 const size_t kBufSize = 1 << 16;
658 CTempMidBuffer buf(kBufSize);
659 if (!buf.Buf)
660 return E_OUTOFMEMORY;
661
662 NIO::CInFile inFile;
663 NIO::COutFile outFile;
664
665 if (!inFile.Open(_CopyFile_Path))
666 return SendMessageError_with_LastError("Open error", _CopyFile_Path);
667
668 for (;;)
669 {
670 UInt32 num;
671
672 if (!inFile.Read(buf.Buf, kBufSize, num))
673 return SendMessageError_with_LastError("Read error", _CopyFile_Path);
674
675 if (num == 0)
676 return S_OK;
677
678
679 RINOK(WriteStream(outStream, buf.Buf, num));
680 }
681}
682*/
683
684
685HRESULT CArchiveExtractCallback::ReadLink()
686{
687 IInArchive *archive = _arc->Archive;
688 const UInt32 index = _index;
689 _link.Clear();
690
691 {
692 NCOM::CPropVariant prop;
693 RINOK(archive->GetProperty(index, kpidHardLink, &prop));
694 if (prop.vt == VT_BSTR)
695 {
696 _link.isHardLink = true;
697 // _link.isCopyLink = false;
698 _link.isRelative = false; // RAR5, TAR: hard links are from root folder of archive
699 _link.linkPath.SetFromBstr(prop.bstrVal);
700 }
701 else if (prop.vt != VT_EMPTY)
702 return E_FAIL;
703 }
704
705 /*
706 {
707 NCOM::CPropVariant prop;
708 RINOK(archive->GetProperty(index, kpidCopyLink, &prop));
709 if (prop.vt == VT_BSTR)
710 {
711 _link.isHardLink = false;
712 _link.isCopyLink = true;
713 _link.isRelative = false; // RAR5: copy links are from root folder of archive
714 _link.linkPath.SetFromBstr(prop.bstrVal);
715 }
716 else if (prop.vt != VT_EMPTY)
717 return E_FAIL;
718 }
719 */
720
721 {
722 NCOM::CPropVariant prop;
723 RINOK(archive->GetProperty(index, kpidSymLink, &prop));
724 if (prop.vt == VT_BSTR)
725 {
726 _link.isHardLink = false;
727 // _link.isCopyLink = false;
728 _link.isRelative = true; // RAR5, TAR: symbolic links can be relative
729 _link.linkPath.SetFromBstr(prop.bstrVal);
730 }
731 else if (prop.vt != VT_EMPTY)
732 return E_FAIL;
733 }
734
735 NtReparse_Data = NULL;
736 NtReparse_Size = 0;
737
738 if (_link.linkPath.IsEmpty() && _arc->GetRawProps)
739 {
740 const void *data;
741 UInt32 dataSize;
742 UInt32 propType;
743
744 _arc->GetRawProps->GetRawProp(_index, kpidNtReparse, &data, &dataSize, &propType);
745
746 // if (dataSize == 1234567) // for debug: unpacking without reparse
747 if (dataSize != 0)
748 {
749 if (propType != NPropDataType::kRaw)
750 return E_FAIL;
751
752 // 21.06: we need kpidNtReparse in linux for wim archives created in Windows
753 // #ifdef _WIN32
754
755 NtReparse_Data = data;
756 NtReparse_Size = dataSize;
757
758 CReparseAttr reparse;
759 bool isOkReparse = reparse.Parse((const Byte *)data, dataSize);
760 if (isOkReparse)
761 {
762 _link.isHardLink = false;
763 // _link.isCopyLink = false;
764 _link.linkPath = reparse.GetPath();
765 _link.isJunction = reparse.IsMountPoint();
766
767 if (reparse.IsSymLink_WSL())
768 {
769 _link.isWSL = true;
770 _link.isRelative = reparse.IsRelative_WSL();
771 }
772 else
773 _link.isRelative = reparse.IsRelative_Win();
774
775 // const AString s = GetAnsiString(_link.linkPath);
776 // printf("\n_link.linkPath: %s\n", s.Ptr());
777
778 #ifndef _WIN32
779 _link.linkPath.Replace(L'\\', WCHAR_PATH_SEPARATOR);
780 #endif
781 }
782 // #endif
783 }
784 }
785
786 if (_link.linkPath.IsEmpty())
787 return S_OK;
788
789 {
790 #ifdef _WIN32
791 _link.linkPath.Replace(L'/', WCHAR_PATH_SEPARATOR);
792 #endif
793
794 // rar5 uses "\??\" prefix for absolute links
795 if (_link.linkPath.IsPrefixedBy(WSTRING_PATH_SEPARATOR L"??" WSTRING_PATH_SEPARATOR))
796 {
797 _link.isRelative = false;
798 _link.linkPath.DeleteFrontal(4);
799 }
800
801 for (;;)
802 // while (NName::IsAbsolutePath(linkPath))
803 {
804 unsigned n = NName::GetRootPrefixSize(_link.linkPath);
805 if (n == 0)
806 break;
807 _link.isRelative = false;
808 _link.linkPath.DeleteFrontal(n);
809 }
810 }
811
812 if (_link.linkPath.IsEmpty())
813 return S_OK;
814
815 if (!_link.isRelative && _removePathParts.Size() != 0)
816 {
817 UStringVector pathParts;
818 SplitPathToParts(_link.linkPath, pathParts);
819 bool badPrefix = false;
820 FOR_VECTOR (i, _removePathParts)
821 {
822 if (CompareFileNames(_removePathParts[i], pathParts[i]) != 0)
823 {
824 badPrefix = true;
825 break;
826 }
827 }
828 if (!badPrefix)
829 pathParts.DeleteFrontal(_removePathParts.Size());
830 _link.linkPath = MakePathFromParts(pathParts);
831 }
832
833 /*
834 if (!_link.linkPath.IsEmpty())
835 {
836 printf("\n_link %s to -> %s\n", GetOemString(_item.Path).Ptr(), GetOemString(_link.linkPath).Ptr());
837 }
838 */
839
840 return S_OK;
841}
842
843#endif // SUPPORT_LINKS
844
845
846
847HRESULT CArchiveExtractCallback::Read_fi_Props()
848{
849 IInArchive *archive = _arc->Archive;
850 const UInt32 index = _index;
851
852 _fi.AttribDefined = false;
853
854 {
855 NCOM::CPropVariant prop;
856 RINOK(archive->GetProperty(index, kpidPosixAttrib, &prop));
857 if (prop.vt == VT_UI4)
858 {
859 _fi.SetFromPosixAttrib(prop.ulVal);
860 }
861 else if (prop.vt != VT_EMPTY)
862 return E_FAIL;
863 }
864
865 {
866 NCOM::CPropVariant prop;
867 RINOK(archive->GetProperty(index, kpidAttrib, &prop));
868 if (prop.vt == VT_UI4)
869 {
870 _fi.Attrib = prop.ulVal;
871 _fi.AttribDefined = true;
872 }
873 else if (prop.vt != VT_EMPTY)
874 return E_FAIL;
875 }
876
877 RINOK(GetTime(index, kpidCTime, _fi.CTime, _fi.CTimeDefined));
878 RINOK(GetTime(index, kpidATime, _fi.ATime, _fi.ATimeDefined));
879 RINOK(GetTime(index, kpidMTime, _fi.MTime, _fi.MTimeDefined));
880 return S_OK;
881}
882
883
884
885void CArchiveExtractCallback::CorrectPathParts()
886{
887 UStringVector &pathParts = _item.PathParts;
888
889 #ifdef SUPPORT_ALT_STREAMS
890 if (!_item.IsAltStream
891 || !pathParts.IsEmpty()
892 || !(_removePartsForAltStreams || _pathMode == NExtract::NPathMode::kNoPathsAlt))
893 #endif
894 Correct_FsPath(_pathMode == NExtract::NPathMode::kAbsPaths, _keepAndReplaceEmptyDirPrefixes, pathParts, _item.MainIsDir);
895
896 #ifdef SUPPORT_ALT_STREAMS
897
898 if (_item.IsAltStream)
899 {
900 UString s (_item.AltStreamName);
901 Correct_AltStream_Name(s);
902 bool needColon = true;
903
904 if (pathParts.IsEmpty())
905 {
906 pathParts.AddNew();
907 if (_removePartsForAltStreams || _pathMode == NExtract::NPathMode::kNoPathsAlt)
908 needColon = false;
909 }
910 #ifdef _WIN32
911 else if (_pathMode == NExtract::NPathMode::kAbsPaths &&
912 NWildcard::GetNumPrefixParts_if_DrivePath(pathParts) == pathParts.Size())
913 pathParts.AddNew();
914 #endif
915
916 UString &name = pathParts.Back();
917 if (needColon)
918 name += (char)(_ntOptions.ReplaceColonForAltStream ? '_' : ':');
919 name += s;
920 }
921
922 #endif // SUPPORT_ALT_STREAMS
923}
924
925
926
927void CArchiveExtractCallback::CreateFolders()
928{
929 // 21.04 : we don't change original (_item.PathParts) here
930 UStringVector pathParts = _item.PathParts;
931
932 if (!_item.IsDir)
933 {
934 if (!pathParts.IsEmpty())
935 pathParts.DeleteBack();
936 }
937
938 if (pathParts.IsEmpty())
939 return;
940
941 FString fullPathNew;
942 CreateComplexDirectory(pathParts, fullPathNew);
943
944 if (!_item.IsDir)
945 return;
946
947 if (_itemFailure)
948 return;
949
950 CDirPathTime pt;
951
952 pt.CTime = _fi.CTime;
953 pt.CTimeDefined = (WriteCTime && _fi.CTimeDefined);
954
955 pt.ATime = _fi.ATime;
956 pt.ATimeDefined = (WriteATime && _fi.ATimeDefined);
957
958 pt.MTimeDefined = false;
959
960 if (WriteMTime)
961 {
962 if (_fi.MTimeDefined)
963 {
964 pt.MTime = _fi.MTime;
965 pt.MTimeDefined = true;
966 }
967 else if (_arc->MTimeDefined)
968 {
969 pt.MTime = _arc->MTime;
970 pt.MTimeDefined = true;
971 }
972 }
973
974 if (pt.MTimeDefined || pt.ATimeDefined || pt.CTimeDefined)
975 {
976 pt.Path = fullPathNew;
977 pt.SetDirTime();
978 _extractedFolders.Add(pt);
979 }
980}
981
982
983
984/*
985 CheckExistFile(fullProcessedPath)
986 it can change: fullProcessedPath, _isRenamed, _overwriteMode
987 (needExit = true) means that we must exit GetStream() even for S_OK result.
988*/
989
990HRESULT CArchiveExtractCallback::CheckExistFile(FString &fullProcessedPath, bool &needExit)
991{
992 needExit = true; // it was set already before
993
994 NFind::CFileInfo fileInfo;
995
996 if (fileInfo.Find(fullProcessedPath))
997 {
998 if (_overwriteMode == NExtract::NOverwriteMode::kSkip)
999 return S_OK;
1000
1001 if (_overwriteMode == NExtract::NOverwriteMode::kAsk)
1002 {
1003 const int slashPos = fullProcessedPath.ReverseFind_PathSepar();
1004 const FString realFullProcessedPath = fullProcessedPath.Left((unsigned)(slashPos + 1)) + fileInfo.Name;
1005
1006 /* (fileInfo) can be symbolic link.
1007 we can show final file properties here. */
1008
1009 Int32 overwriteResult;
1010 RINOK(_extractCallback2->AskOverwrite(
1011 fs2us(realFullProcessedPath), &fileInfo.MTime, &fileInfo.Size, _item.Path,
1012 _fi.MTimeDefined ? &_fi.MTime : NULL,
1013 _curSizeDefined ? &_curSize : NULL,
1014 &overwriteResult))
1015
1016 switch (overwriteResult)
1017 {
1018 case NOverwriteAnswer::kCancel:
1019 return E_ABORT;
1020 case NOverwriteAnswer::kNo:
1021 return S_OK;
1022 case NOverwriteAnswer::kNoToAll:
1023 _overwriteMode = NExtract::NOverwriteMode::kSkip;
1024 return S_OK;
1025
1026 case NOverwriteAnswer::kYes:
1027 break;
1028 case NOverwriteAnswer::kYesToAll:
1029 _overwriteMode = NExtract::NOverwriteMode::kOverwrite;
1030 break;
1031 case NOverwriteAnswer::kAutoRename:
1032 _overwriteMode = NExtract::NOverwriteMode::kRename;
1033 break;
1034 default:
1035 return E_FAIL;
1036 }
1037 } // NExtract::NOverwriteMode::kAsk
1038
1039 if (_overwriteMode == NExtract::NOverwriteMode::kRename)
1040 {
1041 if (!AutoRenamePath(fullProcessedPath))
1042 {
1043 RINOK(SendMessageError(kCantAutoRename, fullProcessedPath));
1044 return E_FAIL;
1045 }
1046 _isRenamed = true;
1047 }
1048 else if (_overwriteMode == NExtract::NOverwriteMode::kRenameExisting)
1049 {
1050 FString existPath (fullProcessedPath);
1051 if (!AutoRenamePath(existPath))
1052 {
1053 RINOK(SendMessageError(kCantAutoRename, fullProcessedPath));
1054 return E_FAIL;
1055 }
1056 // MyMoveFile can rename folders. So it's OK to use it for folders too
1057 if (!MyMoveFile(fullProcessedPath, existPath))
1058 {
1059 HRESULT errorCode = GetLastError_noZero_HRESULT();
1060 RINOK(SendMessageError2(errorCode, kCantRenameFile, existPath, fullProcessedPath));
1061 return E_FAIL;
1062 }
1063 }
1064 else // not Rename*
1065 {
1066 if (fileInfo.IsDir())
1067 {
1068 // do we need to delete all files in folder?
1069 if (!RemoveDir(fullProcessedPath))
1070 {
1071 RINOK(SendMessageError_with_LastError(kCantDeleteOutputDir, fullProcessedPath));
1072 return S_OK;
1073 }
1074 }
1075 else // fileInfo is not Dir
1076 {
1077 if (NFind::DoesFileExist_Raw(fullProcessedPath))
1078 if (!DeleteFileAlways(fullProcessedPath))
1079 if (GetLastError() != ERROR_FILE_NOT_FOUND) // check it in linux
1080 {
1081 RINOK(SendMessageError_with_LastError(kCantDeleteOutputFile, fullProcessedPath));
1082 return S_OK;
1083 // return E_FAIL;
1084 }
1085 } // fileInfo is not Dir
1086 } // not Rename*
1087 }
1088 else // not Find(fullProcessedPath)
1089 {
1090 #if defined(_WIN32) && !defined(UNDER_CE)
1091 // we need to clear READ-ONLY of parent before creating alt stream
1092 int colonPos = NName::FindAltStreamColon(fullProcessedPath);
1093 if (colonPos >= 0 && fullProcessedPath[(unsigned)colonPos + 1] != 0)
1094 {
1095 FString parentFsPath (fullProcessedPath);
1096 parentFsPath.DeleteFrom((unsigned)colonPos);
1097 NFind::CFileInfo parentFi;
1098 if (parentFi.Find(parentFsPath))
1099 {
1100 if (parentFi.IsReadOnly())
1101 SetFileAttrib(parentFsPath, parentFi.Attrib & ~(DWORD)FILE_ATTRIBUTE_READONLY);
1102 }
1103 }
1104 #endif // defined(_WIN32) && !defined(UNDER_CE)
1105 }
1106
1107 needExit = false;
1108 return S_OK;
1109}
1110
1111
1112
1113
1114
1115
1116HRESULT CArchiveExtractCallback::GetExtractStream(CMyComPtr<ISequentialOutStream> &outStreamLoc, bool &needExit)
1117{
1118 needExit = true;
1119
1120 RINOK(Read_fi_Props());
1121
1122 #ifdef SUPPORT_LINKS
1123 IInArchive *archive = _arc->Archive;
1124 #endif
1125
1126 const UInt32 index = _index;
1127
1128 bool isAnti = false;
1129 RINOK(_arc->IsItemAnti(index, isAnti));
1130
1131 CorrectPathParts();
1132 UString processedPath (MakePathFromParts(_item.PathParts));
1133
1134 if (!isAnti)
1135 {
1136 // 21.04: CreateFolders doesn't change (_item.PathParts)
1137 CreateFolders();
1138 }
1139
1140 FString fullProcessedPath (us2fs(processedPath));
1141 if (_pathMode != NExtract::NPathMode::kAbsPaths
1142 || !NName::IsAbsolutePath(processedPath))
1143 {
1144 fullProcessedPath = MakePath_from_2_Parts(_dirPathPrefix, fullProcessedPath);
1145 }
1146
1147 #ifdef SUPPORT_ALT_STREAMS
1148 if (_item.IsAltStream && _item.ParentIndex != (UInt32)(Int32)-1)
1149 {
1150 int renIndex = _renamedFiles.FindInSorted(CIndexToPathPair(_item.ParentIndex));
1151 if (renIndex >= 0)
1152 {
1153 const CIndexToPathPair &pair = _renamedFiles[(unsigned)renIndex];
1154 fullProcessedPath = pair.Path;
1155 fullProcessedPath += ':';
1156 UString s (_item.AltStreamName);
1157 Correct_AltStream_Name(s);
1158 fullProcessedPath += us2fs(s);
1159 }
1160 }
1161 #endif // SUPPORT_ALT_STREAMS
1162
1163 if (_item.IsDir)
1164 {
1165 _diskFilePath = fullProcessedPath;
1166 if (isAnti)
1167 RemoveDir(_diskFilePath);
1168 #ifdef SUPPORT_LINKS
1169 if (_link.linkPath.IsEmpty())
1170 #endif
1171 {
1172 if (!isAnti)
1173 SetAttrib();
1174 return S_OK;
1175 }
1176 }
1177 else if (!_isSplit)
1178 {
1179 RINOK(CheckExistFile(fullProcessedPath, needExit));
1180 if (needExit)
1181 return S_OK;
1182 needExit = true;
1183 }
1184
1185 _diskFilePath = fullProcessedPath;
1186
1187
1188 if (isAnti)
1189 {
1190 needExit = false;
1191 return S_OK;
1192 }
1193
1194 // not anti
1195
1196 #ifdef SUPPORT_LINKS
1197
1198 if (!_link.linkPath.IsEmpty())
1199 {
1200 #ifndef UNDER_CE
1201 {
1202 bool linkWasSet = false;
1203 RINOK(SetFromLinkPath(fullProcessedPath, _link, linkWasSet));
1204 if (linkWasSet)
1205 {
1206 _isSymLinkCreated = _link.IsSymLink();
1207 SetAttrib();
1208 // printf("\nlinkWasSet %s\n", GetAnsiString(_diskFilePath));
1209 }
1210 }
1211 #endif // UNDER_CE
1212
1213 // if (_CopyFile_Path.IsEmpty())
1214 {
1215 needExit = false;
1216 return S_OK;
1217 }
1218 }
1219
1220 if (!_hardLinks.IDs.IsEmpty() && !_item.IsAltStream)
1221 {
1222 CHardLinkNode h;
1223 bool defined;
1224 RINOK(Archive_Get_HardLinkNode(archive, index, h, defined));
1225 if (defined)
1226 {
1227 int linkIndex = _hardLinks.IDs.FindInSorted2(h);
1228 if (linkIndex >= 0)
1229 {
1230 FString &hl = _hardLinks.Links[(unsigned)linkIndex];
1231 if (hl.IsEmpty())
1232 hl = fullProcessedPath;
1233 else
1234 {
1235 if (!MyCreateHardLink(fullProcessedPath, hl))
1236 {
1237 HRESULT errorCode = GetLastError_noZero_HRESULT();
1238 RINOK(SendMessageError2(errorCode, kCantCreateHardLink, fullProcessedPath, hl));
1239 return S_OK;
1240 }
1241
1242 // printf("\nHard linkWasSet Archive_Get_HardLinkNode %s\n", GetAnsiString(_diskFilePath));
1243 // _needSetAttrib = true; // do we need to set attribute ?
1244 SetAttrib();
1245 needExit = false;
1246 return S_OK;
1247 }
1248 }
1249 }
1250 }
1251
1252 #endif // SUPPORT_LINKS
1253
1254
1255 // ---------- CREATE WRITE FILE -----
1256
1257 _outFileStreamSpec = new COutFileStream;
1258 CMyComPtr<ISequentialOutStream> outFileStream_Loc(_outFileStreamSpec);
1259
1260 if (!_outFileStreamSpec->Open(fullProcessedPath, _isSplit ? OPEN_ALWAYS: CREATE_ALWAYS))
1261 {
1262 // if (::GetLastError() != ERROR_FILE_EXISTS || !isSplit)
1263 {
1264 RINOK(SendMessageError_with_LastError(kCantOpenOutFile, fullProcessedPath));
1265 return S_OK;
1266 }
1267 }
1268
1269 _needSetAttrib = true;
1270
1271 bool is_SymLink_in_Data = false;
1272
1273 if (_curSizeDefined && _curSize > 0 && _curSize < (1 << 12))
1274 {
1275 if (_fi.IsLinuxSymLink())
1276 {
1277 is_SymLink_in_Data = true;
1278 _is_SymLink_in_Data_Linux = true;
1279 }
1280 else if (_fi.IsReparse())
1281 {
1282 is_SymLink_in_Data = true;
1283 _is_SymLink_in_Data_Linux = false;
1284 }
1285 }
1286
1287 if (is_SymLink_in_Data)
1288 {
1289 _outMemBuf.Alloc((size_t)_curSize);
1290 _bufPtrSeqOutStream_Spec = new CBufPtrSeqOutStream;
1291 _bufPtrSeqOutStream = _bufPtrSeqOutStream_Spec;
1292 _bufPtrSeqOutStream_Spec->Init(_outMemBuf, _outMemBuf.Size());
1293 outStreamLoc = _bufPtrSeqOutStream;
1294 }
1295 else // not reprase
1296 {
1297 if (_ntOptions.PreAllocateOutFile && !_isSplit && _curSizeDefined && _curSize > (1 << 12))
1298 {
1299 // UInt64 ticks = GetCpuTicks();
1300 _fileLength_that_WasSet = _curSize;
1301 bool res = _outFileStreamSpec->File.SetLength(_curSize);
1302 _fileLengthWasSet = res;
1303
1304 // ticks = GetCpuTicks() - ticks;
1305 // printf("\nticks = %10d\n", (unsigned)ticks);
1306 if (!res)
1307 {
1308 RINOK(SendMessageError_with_LastError(kCantSetFileLen, fullProcessedPath));
1309 }
1310
1311 /*
1312 _outFileStreamSpec->File.Close();
1313 ticks = GetCpuTicks() - ticks;
1314 printf("\nticks = %10d\n", (unsigned)ticks);
1315 return S_FALSE;
1316 */
1317
1318 /*
1319 File.SetLength() on FAT (xp64): is fast, but then File.Close() can be slow,
1320 if we don't write any data.
1321 File.SetLength() for remote share file (exFAT) can be slow in some cases,
1322 and the Windows can return "network error" after 1 minute,
1323 while remote file still can grow.
1324 We need some way to detect such bad cases and disable PreAllocateOutFile mode.
1325 */
1326
1327 res = _outFileStreamSpec->SeekToBegin_bool();
1328 if (!res)
1329 {
1330 RINOK(SendMessageError_with_LastError("Cannot seek to begin of file", fullProcessedPath));
1331 }
1332 } // PreAllocateOutFile
1333
1334 #ifdef SUPPORT_ALT_STREAMS
1335 if (_isRenamed && !_item.IsAltStream)
1336 {
1337 CIndexToPathPair pair(index, fullProcessedPath);
1338 unsigned oldSize = _renamedFiles.Size();
1339 unsigned insertIndex = _renamedFiles.AddToUniqueSorted(pair);
1340 if (oldSize == _renamedFiles.Size())
1341 _renamedFiles[insertIndex].Path = fullProcessedPath;
1342 }
1343 #endif // SUPPORT_ALT_STREAMS
1344
1345 if (_isSplit)
1346 {
1347 RINOK(_outFileStreamSpec->Seek((Int64)_position, STREAM_SEEK_SET, NULL));
1348 }
1349 outStreamLoc = outFileStream_Loc;
1350 } // if not reprase
1351
1352 _outFileStream = outFileStream_Loc;
1353
1354 needExit = false;
1355 return S_OK;
1356}
1357
1358
1359
1360HRESULT CArchiveExtractCallback::GetItem(UInt32 index)
1361{
1362 #ifndef _SFX
1363 _item._use_baseParentFolder_mode = _use_baseParentFolder_mode;
1364 if (_use_baseParentFolder_mode)
1365 {
1366 _item._baseParentFolder = (int)_baseParentFolder;
1367 if (_pathMode == NExtract::NPathMode::kFullPaths ||
1368 _pathMode == NExtract::NPathMode::kAbsPaths)
1369 _item._baseParentFolder = -1;
1370 }
1371 #endif // _SFX
1372
1373 #ifdef SUPPORT_ALT_STREAMS
1374 _item.WriteToAltStreamIfColon = _ntOptions.WriteToAltStreamIfColon;
1375 #endif
1376
1377 return _arc->GetItem(index, _item);
1378}
1379
1380
1381STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode)
1382{
1383 COM_TRY_BEGIN
1384
1385 *outStream = NULL;
1386
1387 #ifndef _SFX
1388 if (_hashStream)
1389 _hashStreamSpec->ReleaseStream();
1390 _hashStreamWasUsed = false;
1391 #endif
1392
1393 _outFileStream.Release();
1394 _bufPtrSeqOutStream.Release();
1395
1396 _encrypted = false;
1397 _position = 0;
1398 _isSplit = false;
1399
1400 _curSize = 0;
1401 _curSizeDefined = false;
1402 _fileLengthWasSet = false;
1403 _fileLength_that_WasSet = 0;
1404 _index = index;
1405
1406 _diskFilePath.Empty();
1407
1408 _isRenamed = false;
1409
1410 // _fi.Clear();
1411
1412 // _is_SymLink_in_Data = false;
1413 _is_SymLink_in_Data_Linux = false;
1414
1415 _needSetAttrib = false;
1416 _isSymLinkCreated = false;
1417 _itemFailure = false;
1418
1419 #ifdef SUPPORT_LINKS
1420 // _CopyFile_Path.Empty();
1421 _link.Clear();
1422 #endif
1423
1424 _extractMode = false;
1425
1426 switch (askExtractMode)
1427 {
1428 case NArchive::NExtract::NAskMode::kExtract:
1429 if (_testMode)
1430 {
1431 // askExtractMode = NArchive::NExtract::NAskMode::kTest;
1432 }
1433 else
1434 _extractMode = true;
1435 break;
1436 };
1437
1438
1439 IInArchive *archive = _arc->Archive;
1440
1441 RINOK(GetItem(index));
1442
1443 {
1444 NCOM::CPropVariant prop;
1445 RINOK(archive->GetProperty(index, kpidPosition, &prop));
1446 if (prop.vt != VT_EMPTY)
1447 {
1448 if (prop.vt != VT_UI8)
1449 return E_FAIL;
1450 _position = prop.uhVal.QuadPart;
1451 _isSplit = true;
1452 }
1453 }
1454
1455 #ifdef SUPPORT_LINKS
1456 RINOK(ReadLink());
1457 #endif // SUPPORT_LINKS
1458
1459
1460 RINOK(Archive_GetItemBoolProp(archive, index, kpidEncrypted, _encrypted));
1461
1462 RINOK(GetUnpackSize());
1463
1464 #ifdef SUPPORT_ALT_STREAMS
1465 if (!_ntOptions.AltStreams.Val && _item.IsAltStream)
1466 return S_OK;
1467 #endif // SUPPORT_ALT_STREAMS
1468
1469 // we can change (_item.PathParts) in this function
1470 UStringVector &pathParts = _item.PathParts;
1471
1472 if (_wildcardCensor)
1473 {
1474 if (!CensorNode_CheckPath(*_wildcardCensor, _item))
1475 return S_OK;
1476 }
1477
1478 #ifndef _SFX
1479 if (_use_baseParentFolder_mode)
1480 {
1481 if (!pathParts.IsEmpty())
1482 {
1483 unsigned numRemovePathParts = 0;
1484
1485 #ifdef SUPPORT_ALT_STREAMS
1486 if (_pathMode == NExtract::NPathMode::kNoPathsAlt && _item.IsAltStream)
1487 numRemovePathParts = pathParts.Size();
1488 else
1489 #endif
1490 if (_pathMode == NExtract::NPathMode::kNoPaths ||
1491 _pathMode == NExtract::NPathMode::kNoPathsAlt)
1492 numRemovePathParts = pathParts.Size() - 1;
1493 pathParts.DeleteFrontal(numRemovePathParts);
1494 }
1495 }
1496 else
1497 #endif // _SFX
1498 {
1499 if (pathParts.IsEmpty())
1500 {
1501 if (_item.IsDir)
1502 return S_OK;
1503 /*
1504 #ifdef SUPPORT_ALT_STREAMS
1505 if (!_item.IsAltStream)
1506 #endif
1507 return E_FAIL;
1508 */
1509 }
1510
1511 unsigned numRemovePathParts = 0;
1512
1513 switch (_pathMode)
1514 {
1515 case NExtract::NPathMode::kFullPaths:
1516 case NExtract::NPathMode::kCurPaths:
1517 {
1518 if (_removePathParts.IsEmpty())
1519 break;
1520 bool badPrefix = false;
1521
1522 if (pathParts.Size() < _removePathParts.Size())
1523 badPrefix = true;
1524 else
1525 {
1526 if (pathParts.Size() == _removePathParts.Size())
1527 {
1528 if (_removePartsForAltStreams)
1529 {
1530 #ifdef SUPPORT_ALT_STREAMS
1531 if (!_item.IsAltStream)
1532 #endif
1533 badPrefix = true;
1534 }
1535 else
1536 {
1537 if (!_item.MainIsDir)
1538 badPrefix = true;
1539 }
1540 }
1541
1542 if (!badPrefix)
1543 FOR_VECTOR (i, _removePathParts)
1544 {
1545 if (CompareFileNames(_removePathParts[i], pathParts[i]) != 0)
1546 {
1547 badPrefix = true;
1548 break;
1549 }
1550 }
1551 }
1552
1553 if (badPrefix)
1554 {
1555 if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode)
1556 return E_FAIL;
1557 }
1558 else
1559 numRemovePathParts = _removePathParts.Size();
1560 break;
1561 }
1562
1563 case NExtract::NPathMode::kNoPaths:
1564 {
1565 if (!pathParts.IsEmpty())
1566 numRemovePathParts = pathParts.Size() - 1;
1567 break;
1568 }
1569 case NExtract::NPathMode::kNoPathsAlt:
1570 {
1571 #ifdef SUPPORT_ALT_STREAMS
1572 if (_item.IsAltStream)
1573 numRemovePathParts = pathParts.Size();
1574 else
1575 #endif
1576 if (!pathParts.IsEmpty())
1577 numRemovePathParts = pathParts.Size() - 1;
1578 break;
1579 }
1580 /*
1581 case NExtract::NPathMode::kFullPaths:
1582 case NExtract::NPathMode::kAbsPaths:
1583 break;
1584 */
1585 default:
1586 break;
1587 }
1588
1589 pathParts.DeleteFrontal(numRemovePathParts);
1590 }
1591
1592
1593 #ifndef _SFX
1594
1595 if (ExtractToStreamCallback)
1596 {
1597 if (!GetProp)
1598 {
1599 GetProp_Spec = new CGetProp;
1600 GetProp = GetProp_Spec;
1601 }
1602 GetProp_Spec->Arc = _arc;
1603 GetProp_Spec->IndexInArc = index;
1604 UString name (MakePathFromParts(pathParts));
1605
1606 #ifdef SUPPORT_ALT_STREAMS
1607 if (_item.IsAltStream)
1608 {
1609 if (!pathParts.IsEmpty() || (!_removePartsForAltStreams && _pathMode != NExtract::NPathMode::kNoPathsAlt))
1610 name += ':';
1611 name += _item.AltStreamName;
1612 }
1613 #endif
1614
1615 return ExtractToStreamCallback->GetStream7(name, BoolToInt(_item.IsDir), outStream, askExtractMode, GetProp);
1616 }
1617
1618 #endif // _SFX
1619
1620
1621 CMyComPtr<ISequentialOutStream> outStreamLoc;
1622
1623 if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode)
1624 {
1625 if (_stdOutMode)
1626 outStreamLoc = new CStdOutFileStream;
1627 else
1628 {
1629 bool needExit = true;
1630 RINOK(GetExtractStream(outStreamLoc, needExit));
1631 if (needExit)
1632 return S_OK;
1633 }
1634 }
1635
1636 #ifndef _SFX
1637 if (_hashStream)
1638 {
1639 if (askExtractMode == NArchive::NExtract::NAskMode::kExtract ||
1640 askExtractMode == NArchive::NExtract::NAskMode::kTest)
1641 {
1642 _hashStreamSpec->SetStream(outStreamLoc);
1643 outStreamLoc = _hashStream;
1644 _hashStreamSpec->Init(true);
1645 _hashStreamWasUsed = true;
1646 }
1647 }
1648 #endif // _SFX
1649
1650 if (outStreamLoc)
1651 {
1652 /*
1653 #ifdef SUPPORT_LINKS
1654 if (!_CopyFile_Path.IsEmpty())
1655 {
1656 RINOK(PrepareOperation(askExtractMode));
1657 RINOK(MyCopyFile(outStreamLoc));
1658 return SetOperationResult(NArchive::NExtract::NOperationResult::kOK);
1659 }
1660 if (_link.isCopyLink && _testMode)
1661 return S_OK;
1662 #endif
1663 */
1664 *outStream = outStreamLoc.Detach();
1665 }
1666
1667 return S_OK;
1668
1669 COM_TRY_END
1670}
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode)
1683{
1684 COM_TRY_BEGIN
1685
1686 #ifndef _SFX
1687 if (ExtractToStreamCallback)
1688 return ExtractToStreamCallback->PrepareOperation7(askExtractMode);
1689 #endif
1690
1691 _extractMode = false;
1692
1693 switch (askExtractMode)
1694 {
1695 case NArchive::NExtract::NAskMode::kExtract:
1696 if (_testMode)
1697 askExtractMode = NArchive::NExtract::NAskMode::kTest;
1698 else
1699 _extractMode = true;
1700 break;
1701 };
1702
1703 return _extractCallback2->PrepareOperation(_item.Path, BoolToInt(_item.IsDir),
1704 askExtractMode, _isSplit ? &_position: 0);
1705
1706 COM_TRY_END
1707}
1708
1709
1710
1711
1712
1713HRESULT CArchiveExtractCallback::CloseFile()
1714{
1715 if (!_outFileStream)
1716 return S_OK;
1717
1718 HRESULT hres = S_OK;
1719
1720 const UInt64 processedSize = _outFileStreamSpec->ProcessedSize;
1721 if (_fileLengthWasSet && _fileLength_that_WasSet > processedSize)
1722 {
1723 bool res = _outFileStreamSpec->File.SetLength(processedSize);
1724 _fileLengthWasSet = res;
1725 if (!res)
1726 {
1727 HRESULT hres2 = SendMessageError_with_LastError(kCantSetFileLen, us2fs(_item.Path));
1728 if (hres == S_OK)
1729 hres = hres2;
1730 }
1731 }
1732
1733 _curSize = processedSize;
1734 _curSizeDefined = true;
1735
1736 // #ifdef _WIN32
1737 _outFileStreamSpec->SetTime(
1738 (WriteCTime && _fi.CTimeDefined) ? &_fi.CTime : NULL,
1739 (WriteATime && _fi.ATimeDefined) ? &_fi.ATime : NULL,
1740 (WriteMTime && _fi.MTimeDefined) ? &_fi.MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL));
1741 // #endif
1742
1743 RINOK(_outFileStreamSpec->Close());
1744 _outFileStream.Release();
1745 return hres;
1746}
1747
1748
1749#ifdef SUPPORT_LINKS
1750
1751
1752HRESULT CArchiveExtractCallback::SetFromLinkPath(
1753 const FString &fullProcessedPath,
1754 const CLinkInfo &linkInfo,
1755 bool &linkWasSet)
1756{
1757 linkWasSet = false;
1758 if (!_ntOptions.SymLinks.Val && !linkInfo.isHardLink)
1759 return S_OK;
1760
1761 UString relatPath;
1762
1763 /* if (linkInfo.isRelative)
1764 linkInfo.linkPath is final link path that must be stored to file link field
1765 else
1766 linkInfo.linkPath is path from root of archive. So we must add _dirPathPrefix_Full before linkPath.
1767 */
1768
1769 if (linkInfo.isRelative)
1770 relatPath = GetDirPrefixOf(_item.Path);
1771 relatPath += linkInfo.linkPath;
1772
1773 if (!IsSafePath(relatPath))
1774 {
1775 return SendMessageError2(
1776 0, // errorCode
1777 "Dangerous link path was ignored",
1778 us2fs(_item.Path),
1779 us2fs(linkInfo.linkPath)); // us2fs(relatPath)
1780 }
1781
1782 FString existPath;
1783 if (linkInfo.isHardLink /* || linkInfo.IsCopyLink */ || !linkInfo.isRelative)
1784 {
1785 if (!NName::GetFullPath(_dirPathPrefix_Full, us2fs(relatPath), existPath))
1786 {
1787 RINOK(SendMessageError("Incorrect path", us2fs(relatPath)));
1788 }
1789 }
1790 else
1791 {
1792 existPath = us2fs(linkInfo.linkPath);
1793 // printf("\nlinkPath = : %s\n", GetOemString(linkInfo.linkPath).Ptr());
1794 }
1795
1796 if (existPath.IsEmpty())
1797 return SendMessageError("Empty link", fullProcessedPath);
1798
1799 if (linkInfo.isHardLink /* || linkInfo.IsCopyLink */)
1800 {
1801 // if (linkInfo.isHardLink)
1802 {
1803 if (!MyCreateHardLink(fullProcessedPath, existPath))
1804 {
1805 HRESULT errorCode = GetLastError_noZero_HRESULT();
1806 RINOK(SendMessageError2(errorCode, kCantCreateHardLink, fullProcessedPath, existPath));
1807 }
1808 linkWasSet = true;
1809 return S_OK;
1810 }
1811 /*
1812 // IsCopyLink
1813 {
1814 NFind::CFileInfo fi;
1815 if (!fi.Find(existPath))
1816 {
1817 RINOK(SendMessageError2("Cannot find the file for copying", existPath, fullProcessedPath));
1818 }
1819 else
1820 {
1821 if (_curSizeDefined && _curSize == fi.Size)
1822 _CopyFile_Path = existPath;
1823 else
1824 {
1825 RINOK(SendMessageError2("File size collision for file copying", existPath, fullProcessedPath));
1826 }
1827 // RINOK(MyCopyFile(existPath, fullProcessedPath));
1828 }
1829 }
1830 */
1831 }
1832
1833 // is Symbolic link
1834
1835 /*
1836 if (_item.IsDir && !isRelative)
1837 {
1838 // Windows before Vista doesn't support symbolic links.
1839 // we could convert such symbolic links to Junction Points
1840 // isJunction = true;
1841 // convertToAbs = true;
1842 }
1843 */
1844
1845 if (!_ntOptions.SymLinks_AllowDangerous.Val)
1846 {
1847 #ifdef _WIN32
1848 if (_item.IsDir)
1849 #endif
1850 if (linkInfo.isRelative)
1851 {
1852 CLinkLevelsInfo levelsInfo;
1853 levelsInfo.Parse(linkInfo.linkPath);
1854 if (levelsInfo.FinalLevel < 1 || levelsInfo.IsAbsolute)
1855 {
1856 return SendMessageError2(
1857 0, // errorCode
1858 "Dangerous symbolic link path was ignored",
1859 us2fs(_item.Path),
1860 us2fs(linkInfo.linkPath));
1861 }
1862 }
1863 }
1864
1865
1866 #ifdef _WIN32
1867
1868 CByteBuffer data;
1869 // printf("\nFillLinkData(): %s\n", GetOemString(existPath).Ptr());
1870 if (!FillLinkData(data, fs2us(existPath), !linkInfo.isJunction, linkInfo.isWSL))
1871 return SendMessageError("Cannot fill link data", us2fs(_item.Path));
1872
1873 /*
1874 if (NtReparse_Size != data.Size() || memcmp(NtReparse_Data, data, data.Size()) != 0)
1875 {
1876 SendMessageError("reconstructed Reparse is different", fs2us(existPath));
1877 }
1878 */
1879
1880 CReparseAttr attr;
1881 if (!attr.Parse(data, data.Size()))
1882 {
1883 RINOK(SendMessageError("Internal error for symbolic link file", us2fs(_item.Path)));
1884 return S_OK;
1885 }
1886 if (!NFile::NIO::SetReparseData(fullProcessedPath, _item.IsDir, data, (DWORD)data.Size()))
1887 {
1888 RINOK(SendMessageError_with_LastError(kCantCreateSymLink, fullProcessedPath));
1889 return S_OK;
1890 }
1891 linkWasSet = true;
1892
1893 return S_OK;
1894
1895
1896 #else // ! _WIN32
1897
1898 if (!NFile::NIO::SetSymLink(fullProcessedPath, existPath))
1899 {
1900 RINOK(SendMessageError_with_LastError(kCantCreateSymLink, fullProcessedPath));
1901 return S_OK;
1902 }
1903 linkWasSet = true;
1904
1905 return S_OK;
1906
1907 #endif // ! _WIN32
1908}
1909
1910
1911bool CLinkInfo::Parse(const Byte *data, size_t dataSize, bool isLinuxData)
1912{
1913 Clear();
1914 // this->isLinux = isLinuxData;
1915
1916 if (isLinuxData)
1917 {
1918 isJunction = false;
1919 isHardLink = false;
1920 AString utf;
1921 if (dataSize >= (1 << 12))
1922 return false;
1923 utf.SetFrom_CalcLen((const char *)data, (unsigned)dataSize);
1924 UString u;
1925 if (!ConvertUTF8ToUnicode(utf, u))
1926 return false;
1927 linkPath = u;
1928
1929 // in linux symbolic data: we expect that linux separator '/' is used
1930 // if windows link was created, then we also must use linux separator
1931 if (u.IsEmpty())
1932 return false;
1933 wchar_t c = u[0];
1934 isRelative = !IS_PATH_SEPAR(c);
1935 return true;
1936 }
1937
1938 CReparseAttr reparse;
1939 if (!reparse.Parse(data, dataSize))
1940 return false;
1941 isHardLink = false;
1942 // isCopyLink = false;
1943 linkPath = reparse.GetPath();
1944 isJunction = reparse.IsMountPoint();
1945
1946 if (reparse.IsSymLink_WSL())
1947 {
1948 isWSL = true;
1949 isRelative = reparse.IsRelative_WSL();
1950 }
1951 else
1952 isRelative = reparse.IsRelative_Win();
1953
1954 // FIXME !!!
1955 #ifndef _WIN32
1956 linkPath.Replace(L'\\', WCHAR_PATH_SEPARATOR);
1957 #endif
1958
1959 return true;
1960}
1961
1962#endif // SUPPORT_LINKS
1963
1964
1965HRESULT CArchiveExtractCallback::CloseReparseAndFile()
1966{
1967 HRESULT res = S_OK;
1968
1969 #ifdef SUPPORT_LINKS
1970
1971 size_t reparseSize = 0;
1972 bool repraseMode = false;
1973 bool needSetReparse = false;
1974 CLinkInfo linkInfo;
1975
1976 if (_bufPtrSeqOutStream)
1977 {
1978 repraseMode = true;
1979 reparseSize = _bufPtrSeqOutStream_Spec->GetPos();
1980 if (_curSizeDefined && reparseSize == _outMemBuf.Size())
1981 {
1982 /*
1983 CReparseAttr reparse;
1984 DWORD errorCode = 0;
1985 needSetReparse = reparse.Parse(_outMemBuf, reparseSize, errorCode);
1986 if (needSetReparse)
1987 {
1988 UString linkPath = reparse.GetPath();
1989 #ifndef _WIN32
1990 linkPath.Replace(L'\\', WCHAR_PATH_SEPARATOR);
1991 #endif
1992 }
1993 */
1994 needSetReparse = linkInfo.Parse(_outMemBuf, reparseSize, _is_SymLink_in_Data_Linux);
1995 if (!needSetReparse)
1996 res = SendMessageError_with_LastError("Incorrect reparse stream", us2fs(_item.Path));
1997 }
1998 else
1999 {
2000 res = SendMessageError_with_LastError("Unknown reparse stream", us2fs(_item.Path));
2001 }
2002 if (!needSetReparse && _outFileStream)
2003 {
2004 HRESULT res2 = WriteStream(_outFileStream, _outMemBuf, reparseSize);
2005 if (res == S_OK)
2006 res = res2;
2007 }
2008 _bufPtrSeqOutStream.Release();
2009 }
2010
2011 #endif // SUPPORT_LINKS
2012
2013
2014 HRESULT res2 = CloseFile();
2015
2016 if (res == S_OK)
2017 res = res2;
2018
2019 RINOK(res);
2020
2021 #ifdef SUPPORT_LINKS
2022 if (repraseMode)
2023 {
2024 _curSize = reparseSize;
2025 _curSizeDefined = true;
2026
2027 #ifdef SUPPORT_LINKS
2028 if (needSetReparse)
2029 {
2030 // in Linux : we must delete empty file before symbolic link creation
2031 // in Windows : we can create symbolic link even without file deleting
2032 if (!DeleteFileAlways(_diskFilePath))
2033 {
2034 RINOK(SendMessageError_with_LastError("can't delete file", _diskFilePath));
2035 }
2036 {
2037 /*
2038 // for DEBUG ONLY: we can extract sym links as WSL links
2039 // to elimanate (non-admin) errors for sym links.
2040 #ifdef _WIN32
2041 if (!linkInfo.isHardLink && !linkInfo.isJunction)
2042 linkInfo.isWSL = true;
2043 #endif
2044 */
2045 bool linkWasSet = false;
2046 RINOK(SetFromLinkPath(_diskFilePath, linkInfo, linkWasSet));
2047 if (linkWasSet)
2048 _isSymLinkCreated = linkInfo.IsSymLink();
2049 else
2050 _needSetAttrib = false;
2051 }
2052 /*
2053 if (!NFile::NIO::SetReparseData(_diskFilePath, _item.IsDir, ))
2054 {
2055 res = SendMessageError_with_LastError(kCantCreateSymLink, _diskFilePath);
2056 }
2057 */
2058 }
2059 #endif
2060 }
2061 #endif
2062 return res;
2063}
2064
2065
2066void CArchiveExtractCallback::SetAttrib()
2067{
2068 #ifndef _WIN32
2069 // Linux now doesn't support permissions for symlinks
2070 if (_isSymLinkCreated)
2071 return;
2072 #endif
2073
2074 if (_itemFailure
2075 || _diskFilePath.IsEmpty()
2076 || _stdOutMode
2077 || !_extractMode
2078 || !_fi.AttribDefined)
2079 return;
2080
2081 {
2082 // const AString s = GetAnsiString(_diskFilePath);
2083 // printf("\nSetFileAttrib_PosixHighDetect: %s: hex:%x\n", s.Ptr(), _fi.Attrib);
2084 bool res = SetFileAttrib_PosixHighDetect(_diskFilePath, _fi.Attrib);
2085 if (!res)
2086 {
2087 // do we need error message here in Windows and in posix?
2088 SendMessageError_with_LastError("Cannot set file attribute", _diskFilePath);
2089 }
2090 }
2091}
2092
2093
2094STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 opRes)
2095{
2096 COM_TRY_BEGIN
2097
2098 // printf("\nCArchiveExtractCallback::SetOperationResult: %d %s\n", opRes, GetAnsiString(_diskFilePath));
2099
2100 #ifndef _SFX
2101 if (ExtractToStreamCallback)
2102 {
2103 GetUnpackSize();
2104 return ExtractToStreamCallback->SetOperationResult8(opRes, BoolToInt(_encrypted), _curSize);
2105 }
2106 #endif
2107
2108 #ifndef _SFX
2109
2110 if (_hashStreamWasUsed)
2111 {
2112 _hashStreamSpec->_hash->Final(_item.IsDir,
2113 #ifdef SUPPORT_ALT_STREAMS
2114 _item.IsAltStream
2115 #else
2116 false
2117 #endif
2118 , _item.Path);
2119 _curSize = _hashStreamSpec->GetSize();
2120 _curSizeDefined = true;
2121 _hashStreamSpec->ReleaseStream();
2122 _hashStreamWasUsed = false;
2123 }
2124
2125 #endif // _SFX
2126
2127 RINOK(CloseReparseAndFile());
2128
2129 #ifdef _USE_SECURITY_CODE
2130 if (!_stdOutMode && _extractMode && _ntOptions.NtSecurity.Val && _arc->GetRawProps)
2131 {
2132 const void *data;
2133 UInt32 dataSize;
2134 UInt32 propType;
2135 _arc->GetRawProps->GetRawProp(_index, kpidNtSecure, &data, &dataSize, &propType);
2136 if (dataSize != 0)
2137 {
2138 if (propType != NPropDataType::kRaw)
2139 return E_FAIL;
2140 if (CheckNtSecure((const Byte *)data, dataSize))
2141 {
2142 SECURITY_INFORMATION securInfo = DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION;
2143 if (_saclEnabled)
2144 securInfo |= SACL_SECURITY_INFORMATION;
2145 ::SetFileSecurityW(fs2us(_diskFilePath), securInfo, (PSECURITY_DESCRIPTOR)(void *)(const Byte *)(data));
2146 }
2147 }
2148 }
2149 #endif // _USE_SECURITY_CODE
2150
2151 if (!_curSizeDefined)
2152 GetUnpackSize();
2153
2154 if (_curSizeDefined)
2155 {
2156 #ifdef SUPPORT_ALT_STREAMS
2157 if (_item.IsAltStream)
2158 AltStreams_UnpackSize += _curSize;
2159 else
2160 #endif
2161 UnpackSize += _curSize;
2162 }
2163
2164 if (_item.IsDir)
2165 NumFolders++;
2166 #ifdef SUPPORT_ALT_STREAMS
2167 else if (_item.IsAltStream)
2168 NumAltStreams++;
2169 #endif
2170 else
2171 NumFiles++;
2172
2173 if (_needSetAttrib)
2174 SetAttrib();
2175
2176 RINOK(_extractCallback2->SetOperationResult(opRes, BoolToInt(_encrypted)));
2177
2178 return S_OK;
2179
2180 COM_TRY_END
2181}
2182
2183
2184
2185STDMETHODIMP CArchiveExtractCallback::ReportExtractResult(UInt32 indexType, UInt32 index, Int32 opRes)
2186{
2187 if (_folderArchiveExtractCallback2)
2188 {
2189 bool isEncrypted = false;
2190 UString s;
2191
2192 if (indexType == NArchive::NEventIndexType::kInArcIndex && index != (UInt32)(Int32)-1)
2193 {
2194 CReadArcItem item;
2195 RINOK(_arc->GetItem(index, item));
2196 s = item.Path;
2197 RINOK(Archive_GetItemBoolProp(_arc->Archive, index, kpidEncrypted, isEncrypted));
2198 }
2199 else
2200 {
2201 s = '#';
2202 s.Add_UInt32(index);
2203 // if (indexType == NArchive::NEventIndexType::kBlockIndex) {}
2204 }
2205
2206 return _folderArchiveExtractCallback2->ReportExtractResult(opRes, isEncrypted, s);
2207 }
2208
2209 return S_OK;
2210}
2211
2212
2213STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password)
2214{
2215 COM_TRY_BEGIN
2216 if (!_cryptoGetTextPassword)
2217 {
2218 RINOK(_extractCallback2.QueryInterface(IID_ICryptoGetTextPassword,
2219 &_cryptoGetTextPassword));
2220 }
2221 return _cryptoGetTextPassword->CryptoGetTextPassword(password);
2222 COM_TRY_END
2223}
2224
2225
2226// ---------- HASH functions ----------
2227
2228FString CArchiveExtractCallback::Hash_GetFullFilePath()
2229{
2230 // this function changes _item.PathParts.
2231 CorrectPathParts();
2232 const UStringVector &pathParts = _item.PathParts;
2233 const UString processedPath (MakePathFromParts(pathParts));
2234 FString fullProcessedPath (us2fs(processedPath));
2235 if (_pathMode != NExtract::NPathMode::kAbsPaths
2236 || !NName::IsAbsolutePath(processedPath))
2237 {
2238 fullProcessedPath = MakePath_from_2_Parts(
2239 DirPathPrefix_for_HashFiles,
2240 // _dirPathPrefix,
2241 fullProcessedPath);
2242 }
2243 return fullProcessedPath;
2244}
2245
2246
2247STDMETHODIMP CArchiveExtractCallback::GetDiskProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
2248{
2249 COM_TRY_BEGIN
2250 NCOM::CPropVariant prop;
2251 if (propID == kpidSize)
2252 {
2253 RINOK(GetItem(index));
2254 const FString fullProcessedPath = Hash_GetFullFilePath();
2255 NFile::NFind::CFileInfo fi;
2256 if (fi.Find_FollowLink(fullProcessedPath))
2257 if (!fi.IsDir())
2258 prop = (UInt64)fi.Size;
2259 }
2260 prop.Detach(value);
2261 return S_OK;
2262 COM_TRY_END
2263}
2264
2265
2266STDMETHODIMP CArchiveExtractCallback::GetStream2(UInt32 index, ISequentialInStream **inStream, UInt32 mode)
2267{
2268 COM_TRY_BEGIN
2269 *inStream = NULL;
2270 // if (index != _index) return E_FAIL;
2271 if (mode != NUpdateNotifyOp::kHashRead)
2272 return E_FAIL;
2273
2274 RINOK(GetItem(index));
2275 const FString fullProcessedPath = Hash_GetFullFilePath();
2276
2277 CInFileStream *inStreamSpec = new CInFileStream;
2278 CMyComPtr<ISequentialInStream> inStreamRef = inStreamSpec;
2279 inStreamSpec->File.PreserveATime = _ntOptions.PreserveATime;
2280 if (!inStreamSpec->OpenShared(fullProcessedPath, _ntOptions.OpenShareForWrite))
2281 {
2282 RINOK(SendMessageError_with_LastError(kCantOpenInFile, fullProcessedPath));
2283 return S_OK;
2284 }
2285 *inStream = inStreamRef.Detach();
2286 return S_OK;
2287 COM_TRY_END
2288}
2289
2290
2291STDMETHODIMP CArchiveExtractCallback::ReportOperation(
2292 UInt32 /* indexType */, UInt32 /* index */, UInt32 /* op */)
2293{
2294 // COM_TRY_BEGIN
2295 return S_OK;
2296 // COM_TRY_END
2297}
2298
2299
2300// ------------ After Extracting functions ------------
2301
2302void CDirPathSortPair::SetNumSlashes(const FChar *s)
2303{
2304 for (unsigned numSlashes = 0;;)
2305 {
2306 FChar c = *s++;
2307 if (c == 0)
2308 {
2309 Len = numSlashes;
2310 return;
2311 }
2312 if (IS_PATH_SEPAR(c))
2313 numSlashes++;
2314 }
2315}
2316
2317
2318bool CDirPathTime::SetDirTime() const
2319{
2320 return NDir::SetDirTime(Path,
2321 CTimeDefined ? &CTime : NULL,
2322 ATimeDefined ? &ATime : NULL,
2323 MTimeDefined ? &MTime : NULL);
2324}
2325
2326
2327HRESULT CArchiveExtractCallback::SetDirsTimes()
2328{
2329 if (!_arc)
2330 return S_OK;
2331
2332 CRecordVector<CDirPathSortPair> pairs;
2333 pairs.ClearAndSetSize(_extractedFolders.Size());
2334 unsigned i;
2335
2336 for (i = 0; i < _extractedFolders.Size(); i++)
2337 {
2338 CDirPathSortPair &pair = pairs[i];
2339 pair.Index = i;
2340 pair.SetNumSlashes(_extractedFolders[i].Path);
2341 }
2342
2343 pairs.Sort2();
2344
2345 HRESULT res = S_OK;
2346
2347 for (i = 0; i < pairs.Size(); i++)
2348 {
2349 const CDirPathTime &dpt = _extractedFolders[pairs[i].Index];
2350 if (!dpt.SetDirTime())
2351 {
2352 // result = E_FAIL;
2353 // do we need error message here in Windows and in posix?
2354 // SendMessageError_with_LastError("Cannot set directory time", dpt.Path);
2355 }
2356 }
2357
2358 /*
2359 #ifndef _WIN32
2360 for (i = 0; i < _delayedSymLinks.Size(); i++)
2361 {
2362 const CDelayedSymLink &link = _delayedSymLinks[i];
2363 if (!link.Create())
2364 {
2365 if (res == S_OK)
2366 res = GetLastError_noZero_HRESULT();
2367 // res = E_FAIL;
2368 // do we need error message here in Windows and in posix?
2369 SendMessageError_with_LastError("Cannot create Symbolic Link", link._source);
2370 }
2371 }
2372 #endif // _WIN32
2373 */
2374
2375 ClearExtractedDirsInfo();
2376 return res;
2377}
2378
2379
2380HRESULT CArchiveExtractCallback::CloseArc()
2381{
2382 HRESULT res = CloseReparseAndFile();
2383 HRESULT res2 = SetDirsTimes();
2384 if (res == S_OK)
2385 res = res2;
2386 _arc = NULL;
2387 return res;
2388}
diff --git a/CPP/7zip/UI/Common/ArchiveExtractCallback.h b/CPP/7zip/UI/Common/ArchiveExtractCallback.h
new file mode 100644
index 0000000..fe9cb32
--- /dev/null
+++ b/CPP/7zip/UI/Common/ArchiveExtractCallback.h
@@ -0,0 +1,536 @@
1// ArchiveExtractCallback.h
2
3#ifndef __ARCHIVE_EXTRACT_CALLBACK_H
4#define __ARCHIVE_EXTRACT_CALLBACK_H
5
6#include "../../../Common/MyCom.h"
7#include "../../../Common/MyLinux.h"
8#include "../../../Common/Wildcard.h"
9
10#include "../../IPassword.h"
11
12#include "../../Common/FileStreams.h"
13#include "../../Common/ProgressUtils.h"
14#include "../../Common/StreamObjects.h"
15
16#include "../../Archive/IArchive.h"
17
18#include "ExtractMode.h"
19#include "IFileExtractCallback.h"
20#include "OpenArchive.h"
21
22#include "HashCalc.h"
23
24#ifndef _SFX
25
26class COutStreamWithHash:
27 public ISequentialOutStream,
28 public CMyUnknownImp
29{
30 CMyComPtr<ISequentialOutStream> _stream;
31 UInt64 _size;
32 bool _calculate;
33public:
34 IHashCalc *_hash;
35
36 MY_UNKNOWN_IMP
37 STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
38 void SetStream(ISequentialOutStream *stream) { _stream = stream; }
39 void ReleaseStream() { _stream.Release(); }
40 void Init(bool calculate = true)
41 {
42 InitCRC();
43 _size = 0;
44 _calculate = calculate;
45 }
46 void EnableCalc(bool calculate) { _calculate = calculate; }
47 void InitCRC() { _hash->InitForNewFile(); }
48 UInt64 GetSize() const { return _size; }
49};
50
51#endif
52
53struct CExtractNtOptions
54{
55 CBoolPair NtSecurity;
56 CBoolPair SymLinks;
57 CBoolPair SymLinks_AllowDangerous;
58 CBoolPair HardLinks;
59 CBoolPair AltStreams;
60 bool ReplaceColonForAltStream;
61 bool WriteToAltStreamIfColon;
62
63 bool PreAllocateOutFile;
64
65 // used for hash arcs only, when we open external files
66 bool PreserveATime;
67 bool OpenShareForWrite;
68
69 CExtractNtOptions():
70 ReplaceColonForAltStream(false),
71 WriteToAltStreamIfColon(false),
72 PreserveATime(false),
73 OpenShareForWrite(false)
74 {
75 SymLinks.Val = true;
76 SymLinks_AllowDangerous.Val = false;
77 HardLinks.Val = true;
78 AltStreams.Val = true;
79
80 PreAllocateOutFile =
81 #ifdef _WIN32
82 true;
83 #else
84 false;
85 #endif
86 }
87};
88
89#ifndef _SFX
90
91class CGetProp:
92 public IGetProp,
93 public CMyUnknownImp
94{
95public:
96 const CArc *Arc;
97 UInt32 IndexInArc;
98 // UString Name; // relative path
99
100 MY_UNKNOWN_IMP1(IGetProp)
101 INTERFACE_IGetProp(;)
102};
103
104#endif
105
106#ifndef _SFX
107#ifndef UNDER_CE
108
109#define SUPPORT_LINKS
110
111#endif
112#endif
113
114
115#ifdef SUPPORT_LINKS
116
117struct CHardLinkNode
118{
119 UInt64 StreamId;
120 UInt64 INode;
121
122 int Compare(const CHardLinkNode &a) const;
123};
124
125class CHardLinks
126{
127public:
128 CRecordVector<CHardLinkNode> IDs;
129 CObjectVector<FString> Links;
130
131 void Clear()
132 {
133 IDs.Clear();
134 Links.Clear();
135 }
136
137 void PrepareLinks()
138 {
139 while (Links.Size() < IDs.Size())
140 Links.AddNew();
141 }
142};
143
144#endif
145
146#ifdef SUPPORT_ALT_STREAMS
147
148struct CIndexToPathPair
149{
150 UInt32 Index;
151 FString Path;
152
153 CIndexToPathPair(UInt32 index): Index(index) {}
154 CIndexToPathPair(UInt32 index, const FString &path): Index(index), Path(path) {}
155
156 int Compare(const CIndexToPathPair &pair) const
157 {
158 return MyCompare(Index, pair.Index);
159 }
160};
161
162#endif
163
164
165
166struct CDirPathTime
167{
168 FILETIME CTime;
169 FILETIME ATime;
170 FILETIME MTime;
171
172 bool CTimeDefined;
173 bool ATimeDefined;
174 bool MTimeDefined;
175
176 FString Path;
177
178 bool SetDirTime() const;
179};
180
181
182#ifdef SUPPORT_LINKS
183
184struct CLinkInfo
185{
186 // bool isCopyLink;
187 bool isHardLink;
188 bool isJunction;
189 bool isRelative;
190 bool isWSL;
191 UString linkPath;
192
193 bool IsSymLink() const { return !isHardLink; }
194
195 CLinkInfo():
196 // IsCopyLink(false),
197 isHardLink(false),
198 isJunction(false),
199 isRelative(false),
200 isWSL(false)
201 {}
202
203 void Clear()
204 {
205 // IsCopyLink = false;
206 isHardLink = false;
207 isJunction = false;
208 isRelative = false;
209 isWSL = false;
210 linkPath.Empty();
211 }
212
213 bool Parse(const Byte *data, size_t dataSize, bool isLinuxData);
214};
215
216#endif // SUPPORT_LINKS
217
218
219class CArchiveExtractCallback:
220 public IArchiveExtractCallback,
221 public IArchiveExtractCallbackMessage,
222 public ICryptoGetTextPassword,
223 public ICompressProgressInfo,
224 public IArchiveUpdateCallbackFile,
225 public IArchiveGetDiskProperty,
226 public CMyUnknownImp
227{
228 const CArc *_arc;
229 CExtractNtOptions _ntOptions;
230
231 const NWildcard::CCensorNode *_wildcardCensor; // we need wildcard for single pass mode (stdin)
232 CMyComPtr<IFolderArchiveExtractCallback> _extractCallback2;
233 CMyComPtr<ICompressProgressInfo> _compressProgress;
234 CMyComPtr<ICryptoGetTextPassword> _cryptoGetTextPassword;
235 CMyComPtr<IArchiveExtractCallbackMessage> _callbackMessage;
236 CMyComPtr<IFolderArchiveExtractCallback2> _folderArchiveExtractCallback2;
237
238 FString _dirPathPrefix;
239 FString _dirPathPrefix_Full;
240 NExtract::NPathMode::EEnum _pathMode;
241 NExtract::NOverwriteMode::EEnum _overwriteMode;
242 bool _keepAndReplaceEmptyDirPrefixes; // replace them to "_";
243
244 #ifndef _SFX
245
246 CMyComPtr<IFolderExtractToStreamCallback> ExtractToStreamCallback;
247 CGetProp *GetProp_Spec;
248 CMyComPtr<IGetProp> GetProp;
249
250 #endif
251
252 CReadArcItem _item;
253 FString _diskFilePath;
254 UInt64 _position;
255 bool _isSplit;
256
257 bool _extractMode;
258
259 bool WriteCTime;
260 bool WriteATime;
261 bool WriteMTime;
262
263 bool _encrypted;
264
265 struct CProcessedFileInfo
266 {
267 FILETIME CTime;
268 FILETIME ATime;
269 FILETIME MTime;
270 UInt32 Attrib;
271
272 bool CTimeDefined;
273 bool ATimeDefined;
274 bool MTimeDefined;
275 bool AttribDefined;
276
277 bool IsReparse() const
278 {
279 return (AttribDefined && (Attrib & FILE_ATTRIBUTE_REPARSE_POINT) != 0);
280 }
281
282 bool IsLinuxSymLink() const
283 {
284 return (AttribDefined && MY_LIN_S_ISLNK(Attrib >> 16));
285 }
286
287 void SetFromPosixAttrib(UInt32 a)
288 {
289 // here we set only part of combined attribute required by SetFileAttrib() call
290 #ifdef _WIN32
291 // Windows sets FILE_ATTRIBUTE_NORMAL, if we try to set 0 as attribute.
292 Attrib = MY_LIN_S_ISDIR(a) ?
293 FILE_ATTRIBUTE_DIRECTORY :
294 FILE_ATTRIBUTE_ARCHIVE;
295 if ((a & 0222) == 0) // (& S_IWUSR) in p7zip
296 Attrib |= FILE_ATTRIBUTE_READONLY;
297 #else
298 Attrib = (a << 16) | FILE_ATTRIBUTE_UNIX_EXTENSION;
299 #endif
300 AttribDefined = true;
301 }
302 } _fi;
303
304 // bool _is_SymLink_in_Data;
305 bool _is_SymLink_in_Data_Linux; // false = WIN32, true = LINUX
306
307 bool _needSetAttrib;
308 bool _isSymLinkCreated;
309 bool _itemFailure;
310
311 UInt32 _index;
312 UInt64 _curSize;
313 bool _curSizeDefined;
314 bool _fileLengthWasSet;
315 UInt64 _fileLength_that_WasSet;
316
317 COutFileStream *_outFileStreamSpec;
318 CMyComPtr<ISequentialOutStream> _outFileStream;
319
320 CByteBuffer _outMemBuf;
321 CBufPtrSeqOutStream *_bufPtrSeqOutStream_Spec;
322 CMyComPtr<ISequentialOutStream> _bufPtrSeqOutStream;
323
324
325 #ifndef _SFX
326
327 COutStreamWithHash *_hashStreamSpec;
328 CMyComPtr<ISequentialOutStream> _hashStream;
329 bool _hashStreamWasUsed;
330
331 #endif
332
333 bool _removePartsForAltStreams;
334 UStringVector _removePathParts;
335
336 #ifndef _SFX
337 bool _use_baseParentFolder_mode;
338 UInt32 _baseParentFolder;
339 #endif
340
341 bool _stdOutMode;
342 bool _testMode;
343 bool _multiArchives;
344
345 CMyComPtr<ICompressProgressInfo> _localProgress;
346 UInt64 _packTotal;
347
348 UInt64 _progressTotal;
349 bool _progressTotal_Defined;
350
351 CObjectVector<CDirPathTime> _extractedFolders;
352
353 #ifndef _WIN32
354 // CObjectVector<NWindows::NFile::NDir::CDelayedSymLink> _delayedSymLinks;
355 #endif
356
357 #if defined(_WIN32) && !defined(UNDER_CE) && !defined(_SFX)
358 bool _saclEnabled;
359 #endif
360
361 void CreateComplexDirectory(const UStringVector &dirPathParts, FString &fullPath);
362 HRESULT GetTime(UInt32 index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined);
363 HRESULT GetUnpackSize();
364
365 FString Hash_GetFullFilePath();
366
367 void SetAttrib();
368
369public:
370 HRESULT SendMessageError(const char *message, const FString &path);
371 HRESULT SendMessageError_with_LastError(const char *message, const FString &path);
372 HRESULT SendMessageError2(HRESULT errorCode, const char *message, const FString &path1, const FString &path2);
373
374public:
375
376 CLocalProgress *LocalProgressSpec;
377
378 UInt64 NumFolders;
379 UInt64 NumFiles;
380 UInt64 NumAltStreams;
381 UInt64 UnpackSize;
382 UInt64 AltStreams_UnpackSize;
383
384 FString DirPathPrefix_for_HashFiles;
385
386 MY_UNKNOWN_IMP5(
387 IArchiveExtractCallbackMessage,
388 ICryptoGetTextPassword,
389 ICompressProgressInfo,
390 IArchiveUpdateCallbackFile,
391 IArchiveGetDiskProperty
392 )
393
394 INTERFACE_IArchiveExtractCallback(;)
395 INTERFACE_IArchiveExtractCallbackMessage(;)
396 INTERFACE_IArchiveUpdateCallbackFile(;)
397 INTERFACE_IArchiveGetDiskProperty(;)
398
399 STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize);
400
401 STDMETHOD(CryptoGetTextPassword)(BSTR *password);
402
403 CArchiveExtractCallback();
404
405 void InitForMulti(bool multiArchives,
406 NExtract::NPathMode::EEnum pathMode,
407 NExtract::NOverwriteMode::EEnum overwriteMode,
408 bool keepAndReplaceEmptyDirPrefixes)
409 {
410 _multiArchives = multiArchives;
411 _pathMode = pathMode;
412 _overwriteMode = overwriteMode;
413 _keepAndReplaceEmptyDirPrefixes = keepAndReplaceEmptyDirPrefixes;
414 NumFolders = NumFiles = NumAltStreams = UnpackSize = AltStreams_UnpackSize = 0;
415 }
416
417 #ifndef _SFX
418
419 void SetHashMethods(IHashCalc *hash)
420 {
421 if (!hash)
422 return;
423 _hashStreamSpec = new COutStreamWithHash;
424 _hashStream = _hashStreamSpec;
425 _hashStreamSpec->_hash = hash;
426 }
427
428 #endif
429
430 void Init(
431 const CExtractNtOptions &ntOptions,
432 const NWildcard::CCensorNode *wildcardCensor,
433 const CArc *arc,
434 IFolderArchiveExtractCallback *extractCallback2,
435 bool stdOutMode, bool testMode,
436 const FString &directoryPath,
437 const UStringVector &removePathParts, bool removePartsForAltStreams,
438 UInt64 packSize);
439
440
441 #ifdef SUPPORT_LINKS
442
443private:
444 CHardLinks _hardLinks;
445 CLinkInfo _link;
446
447 // FString _CopyFile_Path;
448 // HRESULT MyCopyFile(ISequentialOutStream *outStream);
449 HRESULT Link(const FString &fullProcessedPath);
450 HRESULT ReadLink();
451
452public:
453 // call PrepareHardLinks() after Init()
454 HRESULT PrepareHardLinks(const CRecordVector<UInt32> *realIndices); // NULL means all items
455
456 #endif
457
458
459 #ifdef SUPPORT_ALT_STREAMS
460 CObjectVector<CIndexToPathPair> _renamedFiles;
461 #endif
462
463 // call it after Init()
464
465 #ifndef _SFX
466 void SetBaseParentFolderIndex(UInt32 indexInArc)
467 {
468 _baseParentFolder = indexInArc;
469 _use_baseParentFolder_mode = true;
470 }
471 #endif
472
473 HRESULT CloseArc();
474
475private:
476 void ClearExtractedDirsInfo()
477 {
478 _extractedFolders.Clear();
479 #ifndef _WIN32
480 // _delayedSymLinks.Clear();
481 #endif
482 }
483
484 HRESULT Read_fi_Props();
485 void CorrectPathParts();
486 void CreateFolders();
487
488 bool _isRenamed;
489 HRESULT CheckExistFile(FString &fullProcessedPath, bool &needExit);
490 HRESULT GetExtractStream(CMyComPtr<ISequentialOutStream> &outStreamLoc, bool &needExit);
491 HRESULT GetItem(UInt32 index);
492
493 HRESULT CloseFile();
494 HRESULT CloseReparseAndFile();
495 HRESULT CloseReparseAndFile2();
496 HRESULT SetDirsTimes();
497
498 const void *NtReparse_Data;
499 UInt32 NtReparse_Size;
500
501 #ifdef SUPPORT_LINKS
502 HRESULT SetFromLinkPath(
503 const FString &fullProcessedPath,
504 const CLinkInfo &linkInfo,
505 bool &linkWasSet);
506 #endif
507};
508
509
510struct CArchiveExtractCallback_Closer
511{
512 CArchiveExtractCallback *_ref;
513
514 CArchiveExtractCallback_Closer(CArchiveExtractCallback *ref): _ref(ref) {}
515
516 HRESULT Close()
517 {
518 HRESULT res = S_OK;
519 if (_ref)
520 {
521 res = _ref->CloseArc();
522 _ref = NULL;
523 }
524 return res;
525 }
526
527 ~CArchiveExtractCallback_Closer()
528 {
529 Close();
530 }
531};
532
533
534bool CensorNode_CheckPath(const NWildcard::CCensorNode &node, const CReadArcItem &item);
535
536#endif
diff --git a/CPP/7zip/UI/Common/ArchiveName.cpp b/CPP/7zip/UI/Common/ArchiveName.cpp
new file mode 100644
index 0000000..1baf3e1
--- /dev/null
+++ b/CPP/7zip/UI/Common/ArchiveName.cpp
@@ -0,0 +1,155 @@
1// ArchiveName.cpp
2
3#include "StdAfx.h"
4
5#include "../../../Common/Wildcard.h"
6
7#include "../../../Windows/FileDir.h"
8#include "../../../Windows/FileName.h"
9
10#include "ExtractingFilePath.h"
11#include "ArchiveName.h"
12
13using namespace NWindows;
14using namespace NFile;
15
16static UString CreateArchiveName(const NFind::CFileInfo &fi, bool keepName)
17{
18 FString resultName = fi.Name;
19 if (!fi.IsDir() && !keepName)
20 {
21 int dotPos = resultName.ReverseFind_Dot();
22 if (dotPos > 0)
23 {
24 FString archiveName2 = resultName.Left((unsigned)dotPos);
25 if (archiveName2.ReverseFind_Dot() < 0)
26 resultName = archiveName2;
27 }
28 }
29 return Get_Correct_FsFile_Name(fs2us(resultName));
30}
31
32static FString CreateArchiveName2(const FString &path, bool fromPrev, bool keepName)
33{
34 FString resultName ("Archive");
35 if (fromPrev)
36 {
37 FString dirPrefix;
38 if (NDir::GetOnlyDirPrefix(path, dirPrefix))
39 {
40 if (!dirPrefix.IsEmpty() && IsPathSepar(dirPrefix.Back()))
41 {
42 #if defined(_WIN32) && !defined(UNDER_CE)
43 if (NName::IsDriveRootPath_SuperAllowed(dirPrefix))
44 resultName = dirPrefix[dirPrefix.Len() - 3]; // only letter
45 else
46 #endif
47 {
48 dirPrefix.DeleteBack();
49 NFind::CFileInfo fi;
50 if (fi.Find(dirPrefix))
51 resultName = fi.Name;
52 }
53 }
54 }
55 }
56 else
57 {
58 NFind::CFileInfo fi;
59 if (fi.Find(path))
60 {
61 resultName = fi.Name;
62 if (!fi.IsDir() && !keepName)
63 {
64 int dotPos = resultName.ReverseFind_Dot();
65 if (dotPos > 0)
66 {
67 FString name2 = resultName.Left((unsigned)dotPos);
68 if (name2.ReverseFind_Dot() < 0)
69 resultName = name2;
70 }
71 }
72 }
73 }
74 return resultName;
75}
76
77
78UString CreateArchiveName(const UStringVector &paths, const NFind::CFileInfo *fi)
79{
80 bool keepName = false;
81 /*
82 if (paths.Size() == 1)
83 {
84 const UString &name = paths[0];
85 if (name.Len() > 4)
86 if (CompareFileNames(name.RightPtr(4), L".tar") == 0)
87 keepName = true;
88 }
89 */
90
91 UString name;
92 if (fi)
93 name = CreateArchiveName(*fi, keepName);
94 else
95 {
96 if (paths.IsEmpty())
97 return L"archive";
98 bool fromPrev = (paths.Size() > 1);
99 name = Get_Correct_FsFile_Name(fs2us(CreateArchiveName2(us2fs(paths.Front()), fromPrev, keepName)));
100 }
101
102 UStringVector names;
103
104 {
105 FOR_VECTOR (i, paths)
106 {
107 NFind::CFileInfo fi2;
108 const NFind::CFileInfo *fp;
109 if (fi && paths.Size() == 1)
110 fp = fi;
111 else
112 {
113 if (!fi2.Find(us2fs(paths[i])))
114 continue;
115 fp = &fi2;
116 }
117 names.Add(fs2us(fp->Name));
118 }
119 }
120
121 UString postfix;
122 UInt32 index = 1;
123
124 for (;;)
125 {
126 // we don't want cases when we include archive to itself.
127 // so we find first available name for archive
128 const UString name2 = name + postfix;
129 const UString name2_zip = name2 + L".zip";
130 const UString name2_7z = name2 + L".7z";
131 const UString name2_tar = name2 + L".tar";
132 const UString name2_wim = name2 + L".wim";
133
134 unsigned i = 0;
135
136 for (i = 0; i < names.Size(); i++)
137 {
138 const UString &fname = names[i];
139 if ( 0 == CompareFileNames(fname, name2_zip)
140 || 0 == CompareFileNames(fname, name2_7z)
141 || 0 == CompareFileNames(fname, name2_tar)
142 || 0 == CompareFileNames(fname, name2_wim))
143 break;
144 }
145
146 if (i == names.Size())
147 break;
148 index++;
149 postfix = "_";
150 postfix.Add_UInt32(index);
151 }
152
153 name += postfix;
154 return name;
155}
diff --git a/CPP/7zip/UI/Common/ArchiveName.h b/CPP/7zip/UI/Common/ArchiveName.h
new file mode 100644
index 0000000..0d32645
--- /dev/null
+++ b/CPP/7zip/UI/Common/ArchiveName.h
@@ -0,0 +1,10 @@
1// ArchiveName.h
2
3#ifndef __ARCHIVE_NAME_H
4#define __ARCHIVE_NAME_H
5
6#include "../../../Windows/FileFind.h"
7
8UString CreateArchiveName(const UStringVector &paths, const NWindows::NFile::NFind::CFileInfo *fi = NULL);
9
10#endif
diff --git a/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp b/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp
new file mode 100644
index 0000000..d5926f8
--- /dev/null
+++ b/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp
@@ -0,0 +1,166 @@
1// ArchiveOpenCallback.cpp
2
3#include "StdAfx.h"
4
5#include "../../../Common/ComTry.h"
6
7#include "../../../Windows/FileName.h"
8#include "../../../Windows/PropVariant.h"
9
10#include "../../Common/FileStreams.h"
11
12#include "ArchiveOpenCallback.h"
13
14using namespace NWindows;
15
16STDMETHODIMP COpenCallbackImp::SetTotal(const UInt64 *files, const UInt64 *bytes)
17{
18 COM_TRY_BEGIN
19 if (ReOpenCallback)
20 return ReOpenCallback->SetTotal(files, bytes);
21 if (!Callback)
22 return S_OK;
23 return Callback->Open_SetTotal(files, bytes);
24 COM_TRY_END
25}
26
27STDMETHODIMP COpenCallbackImp::SetCompleted(const UInt64 *files, const UInt64 *bytes)
28{
29 COM_TRY_BEGIN
30 if (ReOpenCallback)
31 return ReOpenCallback->SetCompleted(files, bytes);
32 if (!Callback)
33 return S_OK;
34 return Callback->Open_SetCompleted(files, bytes);
35 COM_TRY_END
36}
37
38STDMETHODIMP COpenCallbackImp::GetProperty(PROPID propID, PROPVARIANT *value)
39{
40 COM_TRY_BEGIN
41 NCOM::CPropVariant prop;
42 if (_subArchiveMode)
43 switch (propID)
44 {
45 case kpidName: prop = _subArchiveName; break;
46 // case kpidSize: prop = _subArchiveSize; break; // we don't use it now
47 }
48 else
49 switch (propID)
50 {
51 case kpidName: prop = fs2us(_fileInfo.Name); break;
52 case kpidIsDir: prop = _fileInfo.IsDir(); break;
53 case kpidSize: prop = _fileInfo.Size; break;
54 case kpidAttrib: prop = (UInt32)_fileInfo.Attrib; break;
55 case kpidCTime: prop = _fileInfo.CTime; break;
56 case kpidATime: prop = _fileInfo.ATime; break;
57 case kpidMTime: prop = _fileInfo.MTime; break;
58 }
59 prop.Detach(value);
60 return S_OK;
61 COM_TRY_END
62}
63
64struct CInFileStreamVol: public CInFileStream
65{
66 unsigned FileNameIndex;
67 COpenCallbackImp *OpenCallbackImp;
68 CMyComPtr<IArchiveOpenCallback> OpenCallbackRef;
69
70 ~CInFileStreamVol()
71 {
72 if (OpenCallbackRef)
73 OpenCallbackImp->FileNames_WasUsed[FileNameIndex] = false;
74 }
75};
76
77
78// from ArchiveExtractCallback.cpp
79bool IsSafePath(const UString &path);
80
81STDMETHODIMP COpenCallbackImp::GetStream(const wchar_t *name, IInStream **inStream)
82{
83 COM_TRY_BEGIN
84 *inStream = NULL;
85
86 if (_subArchiveMode)
87 return S_FALSE;
88 if (Callback)
89 {
90 RINOK(Callback->Open_CheckBreak());
91 }
92
93 UString name2 = name;
94
95
96 #ifndef _SFX
97
98 #ifdef _WIN32
99 name2.Replace(L'/', WCHAR_PATH_SEPARATOR);
100 #endif
101
102 // if (!allowAbsVolPaths)
103 if (!IsSafePath(name2))
104 return S_FALSE;
105
106 #ifdef _WIN32
107 /* WIN32 allows wildcards in Find() function
108 and doesn't allow wildcard in File.Open()
109 so we can work without the following wildcard check here */
110 if (name2.Find(L'*') >= 0)
111 return S_FALSE;
112 {
113 int startPos = 0;
114 if (name2.IsPrefixedBy_Ascii_NoCase("\\\\?\\"))
115 startPos = 3;
116 if (name2.Find(L'?', startPos) >= 0)
117 return S_FALSE;
118 }
119 #endif
120
121 #endif
122
123
124 FString fullPath;
125 if (!NFile::NName::GetFullPath(_folderPrefix, us2fs(name2), fullPath))
126 return S_FALSE;
127 if (!_fileInfo.Find_FollowLink(fullPath))
128 return S_FALSE;
129 if (_fileInfo.IsDir())
130 return S_FALSE;
131 CInFileStreamVol *inFile = new CInFileStreamVol;
132 CMyComPtr<IInStream> inStreamTemp = inFile;
133 if (!inFile->Open(fullPath))
134 {
135 return GetLastError_noZero_HRESULT();
136 }
137
138 FileSizes.Add(_fileInfo.Size);
139 FileNames.Add(name2);
140 inFile->FileNameIndex = FileNames_WasUsed.Add(true);
141 inFile->OpenCallbackImp = this;
142 inFile->OpenCallbackRef = this;
143 // TotalSize += _fileInfo.Size;
144 *inStream = inStreamTemp.Detach();
145 return S_OK;
146 COM_TRY_END
147}
148
149#ifndef _NO_CRYPTO
150STDMETHODIMP COpenCallbackImp::CryptoGetTextPassword(BSTR *password)
151{
152 COM_TRY_BEGIN
153 if (ReOpenCallback)
154 {
155 CMyComPtr<ICryptoGetTextPassword> getTextPassword;
156 ReOpenCallback.QueryInterface(IID_ICryptoGetTextPassword, &getTextPassword);
157 if (getTextPassword)
158 return getTextPassword->CryptoGetTextPassword(password);
159 }
160 if (!Callback)
161 return E_NOTIMPL;
162 PasswordWasAsked = true;
163 return Callback->Open_CryptoGetTextPassword(password);
164 COM_TRY_END
165}
166#endif
diff --git a/CPP/7zip/UI/Common/ArchiveOpenCallback.h b/CPP/7zip/UI/Common/ArchiveOpenCallback.h
new file mode 100644
index 0000000..46b2676
--- /dev/null
+++ b/CPP/7zip/UI/Common/ArchiveOpenCallback.h
@@ -0,0 +1,117 @@
1// ArchiveOpenCallback.h
2
3#ifndef __ARCHIVE_OPEN_CALLBACK_H
4#define __ARCHIVE_OPEN_CALLBACK_H
5
6#include "../../../Common/MyCom.h"
7
8#include "../../../Windows/FileFind.h"
9#include "../../../Windows/FileIO.h"
10
11#ifndef _NO_CRYPTO
12#include "../../IPassword.h"
13#endif
14#include "../../Archive/IArchive.h"
15
16#ifdef _NO_CRYPTO
17
18#define INTERFACE_IOpenCallbackUI_Crypto(x)
19
20#else
21
22#define INTERFACE_IOpenCallbackUI_Crypto(x) \
23 virtual HRESULT Open_CryptoGetTextPassword(BSTR *password) x; \
24 /* virtual HRESULT Open_GetPasswordIfAny(bool &passwordIsDefined, UString &password) x; */ \
25 /* virtual bool Open_WasPasswordAsked() x; */ \
26 /* virtual void Open_Clear_PasswordWasAsked_Flag() x; */ \
27
28#endif
29
30#define INTERFACE_IOpenCallbackUI(x) \
31 virtual HRESULT Open_CheckBreak() x; \
32 virtual HRESULT Open_SetTotal(const UInt64 *files, const UInt64 *bytes) x; \
33 virtual HRESULT Open_SetCompleted(const UInt64 *files, const UInt64 *bytes) x; \
34 virtual HRESULT Open_Finished() x; \
35 INTERFACE_IOpenCallbackUI_Crypto(x)
36
37struct IOpenCallbackUI
38{
39 INTERFACE_IOpenCallbackUI(=0)
40};
41
42class COpenCallbackImp:
43 public IArchiveOpenCallback,
44 public IArchiveOpenVolumeCallback,
45 public IArchiveOpenSetSubArchiveName,
46 #ifndef _NO_CRYPTO
47 public ICryptoGetTextPassword,
48 #endif
49 public CMyUnknownImp
50{
51public:
52 MY_QUERYINTERFACE_BEGIN2(IArchiveOpenVolumeCallback)
53 MY_QUERYINTERFACE_ENTRY(IArchiveOpenSetSubArchiveName)
54 #ifndef _NO_CRYPTO
55 MY_QUERYINTERFACE_ENTRY(ICryptoGetTextPassword)
56 #endif
57 MY_QUERYINTERFACE_END
58 MY_ADDREF_RELEASE
59
60 INTERFACE_IArchiveOpenCallback(;)
61 INTERFACE_IArchiveOpenVolumeCallback(;)
62
63 #ifndef _NO_CRYPTO
64 STDMETHOD(CryptoGetTextPassword)(BSTR *password);
65 #endif
66
67 STDMETHOD(SetSubArchiveName(const wchar_t *name))
68 {
69 _subArchiveMode = true;
70 _subArchiveName = name;
71 // TotalSize = 0;
72 return S_OK;
73 }
74
75private:
76 FString _folderPrefix;
77 NWindows::NFile::NFind::CFileInfo _fileInfo;
78 bool _subArchiveMode;
79 UString _subArchiveName;
80
81public:
82 UStringVector FileNames;
83 CBoolVector FileNames_WasUsed;
84 CRecordVector<UInt64> FileSizes;
85
86 bool PasswordWasAsked;
87
88 IOpenCallbackUI *Callback;
89 CMyComPtr<IArchiveOpenCallback> ReOpenCallback;
90 // UInt64 TotalSize;
91
92 COpenCallbackImp(): _subArchiveMode(false), Callback(NULL) {}
93
94 HRESULT Init2(const FString &folderPrefix, const FString &fileName)
95 {
96 FileNames.Clear();
97 FileNames_WasUsed.Clear();
98 FileSizes.Clear();
99 _subArchiveMode = false;
100 // TotalSize = 0;
101 PasswordWasAsked = false;
102 _folderPrefix = folderPrefix;
103 if (!_fileInfo.Find_FollowLink(_folderPrefix + fileName))
104 {
105 // throw 20121118;
106 return GetLastError_noZero_HRESULT();
107 }
108 return S_OK;
109 }
110
111 bool SetSecondFileInfo(CFSTR newName)
112 {
113 return _fileInfo.Find_FollowLink(newName) && !_fileInfo.IsDir();
114 }
115};
116
117#endif
diff --git a/CPP/7zip/UI/Common/Bench.cpp b/CPP/7zip/UI/Common/Bench.cpp
new file mode 100644
index 0000000..cc0cfd0
--- /dev/null
+++ b/CPP/7zip/UI/Common/Bench.cpp
@@ -0,0 +1,4583 @@
1// Bench.cpp
2
3#include "StdAfx.h"
4
5#include "../../../../C/CpuArch.h"
6
7// #include <stdio.h>
8
9#ifndef _WIN32
10
11#define USE_POSIX_TIME
12#define USE_POSIX_TIME2
13#endif // _WIN32
14
15#ifdef USE_POSIX_TIME
16#include <time.h>
17#include <unistd.h>
18#ifdef USE_POSIX_TIME2
19#include <sys/time.h>
20#include <sys/times.h>
21#endif
22#endif // USE_POSIX_TIME
23
24#ifdef _WIN32
25#define USE_ALLOCA
26#endif
27
28#ifdef USE_ALLOCA
29#ifdef _WIN32
30#include <malloc.h>
31#else
32#include <stdlib.h>
33#endif
34#endif
35
36#include "../../../../C/7zCrc.h"
37#include "../../../../C/RotateDefs.h"
38
39#ifndef _7ZIP_ST
40#include "../../../Windows/Synchronization.h"
41#include "../../../Windows/Thread.h"
42#endif
43
44#include "../../../Windows/FileIO.h"
45#include "../../../Windows/FileFind.h"
46#include "../../../Windows/SystemInfo.h"
47
48#include "../../../Common/IntToString.h"
49#include "../../../Common/MyBuffer2.h"
50#include "../../../Common/StringConvert.h"
51#include "../../../Common/StringToInt.h"
52
53#include "../../Common/MethodProps.h"
54#include "../../Common/StreamObjects.h"
55#include "../../Common/StreamUtils.h"
56
57#include "Bench.h"
58
59using namespace NWindows;
60
61#ifndef _7ZIP_ST
62static const UInt32 k_LZMA = 0x030101;
63#endif
64
65static const UInt64 kComplexInCommands = (UInt64)1 <<
66 #ifdef UNDER_CE
67 31;
68 #else
69 34;
70 #endif
71
72static const UInt32 kComplexInMs = 4000;
73
74static void SetComplexCommandsMs(UInt32 complexInMs,
75 bool isSpecifiedFreq, UInt64 cpuFreq, UInt64 &complexInCommands)
76{
77 complexInCommands = kComplexInCommands;
78 const UInt64 kMinFreq = (UInt64)1000000 * 4;
79 const UInt64 kMaxFreq = (UInt64)1000000 * 20000;
80 if (cpuFreq < kMinFreq && !isSpecifiedFreq)
81 cpuFreq = kMinFreq;
82 if (cpuFreq < kMaxFreq || isSpecifiedFreq)
83 {
84 if (complexInMs != 0)
85 complexInCommands = complexInMs * cpuFreq / 1000;
86 else
87 complexInCommands = cpuFreq >> 2;
88 }
89}
90
91// const UInt64 kBenchmarkUsageMult = 1000000; // for debug
92static const unsigned kBenchmarkUsageMultBits = 16;
93static const UInt64 kBenchmarkUsageMult = 1 << kBenchmarkUsageMultBits;
94
95UInt64 Benchmark_GetUsage_Percents(UInt64 usage)
96{
97 return (100 * usage + kBenchmarkUsageMult / 2) / kBenchmarkUsageMult;
98}
99
100static const unsigned kNumHashDictBits = 17;
101static const UInt32 kFilterUnpackSize = (47 << 10); // + 5; // for test
102
103static const unsigned kOldLzmaDictBits = 32;
104
105// static const size_t kAdditionalSize = (size_t)1 << 32; // for debug
106static const size_t kAdditionalSize = (size_t)1 << 16;
107static const UInt32 kCompressedAdditionalSize = (1 << 10);
108
109static const UInt32 kMaxMethodPropSize = (1 << 6);
110
111
112#define ALLOC_WITH_HRESULT(_buffer_, _size_) \
113 { (_buffer_)->Alloc(_size_); \
114 if (_size_ && !(_buffer_)->IsAllocated()) return E_OUTOFMEMORY; }
115
116
117class CBaseRandomGenerator
118{
119 UInt32 A1;
120 UInt32 A2;
121 UInt32 Salt;
122public:
123 CBaseRandomGenerator(UInt32 salt = 0): Salt(salt) { Init(); }
124 void Init() { A1 = 362436069; A2 = 521288629;}
125 MY_FORCE_INLINE
126 UInt32 GetRnd()
127 {
128 return Salt ^
129 (
130 ((A1 = 36969 * (A1 & 0xffff) + (A1 >> 16)) << 16) +
131 ((A2 = 18000 * (A2 & 0xffff) + (A2 >> 16)) )
132 );
133 }
134};
135
136
137MY_NO_INLINE
138static void RandGen(Byte *buf, size_t size)
139{
140 CBaseRandomGenerator RG;
141 const size_t size4 = size & ~((size_t)3);
142 size_t i;
143 for (i = 0; i < size4; i += 4)
144 {
145 const UInt32 v = RG.GetRnd();
146 SetUi32(buf + i, v);
147 }
148 UInt32 v = RG.GetRnd();
149 for (; i < size; i++)
150 {
151 buf[i] = (Byte)v;
152 v >>= 8;
153 }
154}
155
156
157class CBenchRandomGenerator: public CMidAlignedBuffer
158{
159 static UInt32 GetVal(UInt32 &res, unsigned numBits)
160 {
161 UInt32 val = res & (((UInt32)1 << numBits) - 1);
162 res >>= numBits;
163 return val;
164 }
165
166 static UInt32 GetLen(UInt32 &r)
167 {
168 UInt32 len = GetVal(r, 2);
169 return GetVal(r, 1 + len);
170 }
171
172public:
173
174 void GenerateSimpleRandom(UInt32 salt)
175 {
176 CBaseRandomGenerator rg(salt);
177 const size_t bufSize = Size();
178 Byte *buf = (Byte *)*this;
179 for (size_t i = 0; i < bufSize; i++)
180 buf[i] = (Byte)rg.GetRnd();
181 }
182
183 void GenerateLz(unsigned dictBits, UInt32 salt)
184 {
185 CBaseRandomGenerator rg(salt);
186 size_t pos = 0;
187 size_t rep0 = 1;
188 const size_t bufSize = Size();
189 Byte *buf = (Byte *)*this;
190 unsigned posBits = 1;
191
192 // printf("\n dictBits = %d\n", (UInt32)dictBits);
193 // printf("\n bufSize = 0x%p\n", (const void *)bufSize);
194
195 while (pos < bufSize)
196 {
197 /*
198 if (pos >= ((UInt32)1 << 31))
199 printf(" %x\n", pos);
200 */
201 UInt32 r = rg.GetRnd();
202 if (GetVal(r, 1) == 0 || pos < 1024)
203 buf[pos++] = (Byte)(r & 0xFF);
204 else
205 {
206 UInt32 len;
207 len = 1 + GetLen(r);
208
209 if (GetVal(r, 3) != 0)
210 {
211 len += GetLen(r);
212
213 while (((size_t)1 << posBits) < pos)
214 posBits++;
215
216 unsigned numBitsMax = dictBits;
217 if (numBitsMax > posBits)
218 numBitsMax = posBits;
219
220 const unsigned kAddBits = 6;
221 unsigned numLogBits = 5;
222 if (numBitsMax <= (1 << 4) - 1 + kAddBits)
223 numLogBits = 4;
224
225 for (;;)
226 {
227 const UInt32 ppp = GetVal(r, numLogBits) + kAddBits;
228 r = rg.GetRnd();
229 if (ppp > numBitsMax)
230 continue;
231 // rep0 = GetVal(r, ppp);
232 rep0 = r & (((size_t)1 << ppp) - 1);
233 if (rep0 < pos)
234 break;
235 r = rg.GetRnd();
236 }
237 rep0++;
238 }
239
240 // len *= 300; // for debug
241 {
242 const size_t rem = bufSize - pos;
243 if (len > rem)
244 len = (UInt32)rem;
245 }
246 Byte *dest = buf + pos;
247 const Byte *src = dest - rep0;
248 pos += len;
249 for (UInt32 i = 0; i < len; i++)
250 *dest++ = *src++;
251 }
252 }
253 // printf("\n CRC = %x\n", CrcCalc(buf, bufSize));
254 }
255};
256
257
258class CBenchmarkInStream:
259 public ISequentialInStream,
260 public CMyUnknownImp
261{
262 const Byte *Data;
263 size_t Pos;
264 size_t Size;
265
266public:
267 MY_UNKNOWN_IMP
268 void Init(const Byte *data, size_t size)
269 {
270 Data = data;
271 Size = size;
272 Pos = 0;
273 }
274 bool WasFinished() const { return Pos == Size; }
275 STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
276};
277
278STDMETHODIMP CBenchmarkInStream::Read(void *data, UInt32 size, UInt32 *processedSize)
279{
280 const UInt32 kMaxBlockSize = (1 << 20);
281 if (size > kMaxBlockSize)
282 size = kMaxBlockSize;
283 const size_t remain = Size - Pos;
284 if (size > remain)
285 size = (UInt32)remain;
286
287 if (size != 0)
288 memcpy(data, Data + Pos, size);
289
290 Pos += size;
291 if (processedSize)
292 *processedSize = size;
293 return S_OK;
294}
295
296class CBenchmarkOutStream:
297 public ISequentialOutStream,
298 public CMidAlignedBuffer,
299 public CMyUnknownImp
300{
301 // bool _overflow;
302public:
303 size_t Pos;
304 bool RealCopy;
305 bool CalcCrc;
306 UInt32 Crc;
307
308 // CBenchmarkOutStream(): _overflow(false) {}
309 void Init(bool realCopy, bool calcCrc)
310 {
311 Crc = CRC_INIT_VAL;
312 RealCopy = realCopy;
313 CalcCrc = calcCrc;
314 // _overflow = false;
315 Pos = 0;
316 }
317
318 void InitCrc()
319 {
320 Crc = CRC_INIT_VAL;
321 }
322
323 void Calc(const void *data, size_t size)
324 {
325 Crc = CrcUpdate(Crc, data, size);
326 }
327
328 size_t GetPos() const { return Pos; }
329
330 // void Print() { printf("\n%8d %8d\n", (unsigned)BufferSize, (unsigned)Pos); }
331
332 MY_UNKNOWN_IMP
333 STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
334};
335
336STDMETHODIMP CBenchmarkOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
337{
338 size_t curSize = Size() - Pos;
339 if (curSize > size)
340 curSize = size;
341 if (curSize != 0)
342 {
343 if (RealCopy)
344 memcpy(((Byte *)*this) + Pos, data, curSize);
345 if (CalcCrc)
346 Calc(data, curSize);
347 Pos += curSize;
348 }
349 if (processedSize)
350 *processedSize = (UInt32)curSize;
351 if (curSize != size)
352 {
353 // _overflow = true;
354 return E_FAIL;
355 }
356 return S_OK;
357}
358
359
360class CCrcOutStream:
361 public ISequentialOutStream,
362 public CMyUnknownImp
363{
364public:
365 bool CalcCrc;
366 UInt32 Crc;
367 UInt64 Pos;
368
369 MY_UNKNOWN_IMP
370
371 CCrcOutStream(): CalcCrc(true) {};
372 void Init() { Crc = CRC_INIT_VAL; Pos = 0; }
373 void Calc(const void *data, size_t size)
374 {
375 Crc = CrcUpdate(Crc, data, size);
376 }
377 STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
378};
379
380STDMETHODIMP CCrcOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
381{
382 if (CalcCrc)
383 Calc(data, size);
384 Pos += size;
385 if (processedSize)
386 *processedSize = size;
387 return S_OK;
388}
389
390// #include "../../../../C/My_sys_time.h"
391
392static UInt64 GetTimeCount()
393{
394 #ifdef USE_POSIX_TIME
395 #ifdef USE_POSIX_TIME2
396 timeval v;
397 if (gettimeofday(&v, 0) == 0)
398 return (UInt64)(v.tv_sec) * 1000000 + (UInt64)v.tv_usec;
399 return (UInt64)time(NULL) * 1000000;
400 #else
401 return time(NULL);
402 #endif
403 #else
404 LARGE_INTEGER value;
405 if (::QueryPerformanceCounter(&value))
406 return (UInt64)value.QuadPart;
407 return GetTickCount();
408 #endif
409}
410
411static UInt64 GetFreq()
412{
413 #ifdef USE_POSIX_TIME
414 #ifdef USE_POSIX_TIME2
415 return 1000000;
416 #else
417 return 1;
418 #endif
419 #else
420 LARGE_INTEGER value;
421 if (::QueryPerformanceFrequency(&value))
422 return (UInt64)value.QuadPart;
423 return 1000;
424 #endif
425}
426
427
428#ifdef USE_POSIX_TIME
429
430struct CUserTime
431{
432 UInt64 Sum;
433 clock_t Prev;
434
435 void Init()
436 {
437 // Prev = clock();
438 Sum = 0;
439 Prev = 0;
440 Update();
441 Sum = 0;
442 }
443
444 void Update()
445 {
446 tms t;
447 /* clock_t res = */ times(&t);
448 clock_t newVal = t.tms_utime + t.tms_stime;
449 Sum += (UInt64)(newVal - Prev);
450 Prev = newVal;
451
452 /*
453 clock_t v = clock();
454 if (v != -1)
455 {
456 Sum += v - Prev;
457 Prev = v;
458 }
459 */
460 }
461 UInt64 GetUserTime()
462 {
463 Update();
464 return Sum;
465 }
466};
467
468#else
469
470
471struct CUserTime
472{
473 bool UseTick;
474 DWORD Prev_Tick;
475 UInt64 Prev;
476 UInt64 Sum;
477
478 void Init()
479 {
480 UseTick = false;
481 Prev_Tick = 0;
482 Prev = 0;
483 Sum = 0;
484 Update();
485 Sum = 0;
486 }
487 UInt64 GetUserTime()
488 {
489 Update();
490 return Sum;
491 }
492 void Update();
493};
494
495static inline UInt64 GetTime64(const FILETIME &t) { return ((UInt64)t.dwHighDateTime << 32) | t.dwLowDateTime; }
496
497void CUserTime::Update()
498{
499 DWORD new_Tick = GetTickCount();
500 FILETIME creationTime, exitTime, kernelTime, userTime;
501 if (!UseTick &&
502 #ifdef UNDER_CE
503 ::GetThreadTimes(::GetCurrentThread()
504 #else
505 ::GetProcessTimes(::GetCurrentProcess()
506 #endif
507 , &creationTime, &exitTime, &kernelTime, &userTime))
508 {
509 UInt64 newVal = GetTime64(userTime) + GetTime64(kernelTime);
510 Sum += newVal - Prev;
511 Prev = newVal;
512 }
513 else
514 {
515 UseTick = true;
516 Sum += (UInt64)(new_Tick - (DWORD)Prev_Tick) * 10000;
517 }
518 Prev_Tick = new_Tick;
519}
520
521
522#endif
523
524static UInt64 GetUserFreq()
525{
526 #ifdef USE_POSIX_TIME
527 // return CLOCKS_PER_SEC;
528 return (UInt64)sysconf(_SC_CLK_TCK);
529 #else
530 return 10000000;
531 #endif
532}
533
534class CBenchProgressStatus
535{
536 #ifndef _7ZIP_ST
537 NSynchronization::CCriticalSection CS;
538 #endif
539public:
540 HRESULT Res;
541 bool EncodeMode;
542 void SetResult(HRESULT res)
543 {
544 #ifndef _7ZIP_ST
545 NSynchronization::CCriticalSectionLock lock(CS);
546 #endif
547 Res = res;
548 }
549 HRESULT GetResult()
550 {
551 #ifndef _7ZIP_ST
552 NSynchronization::CCriticalSectionLock lock(CS);
553 #endif
554 return Res;
555 }
556};
557
558struct CBenchInfoCalc
559{
560 CBenchInfo BenchInfo;
561 CUserTime UserTime;
562
563 void SetStartTime();
564 void SetFinishTime(CBenchInfo &dest);
565};
566
567void CBenchInfoCalc::SetStartTime()
568{
569 BenchInfo.GlobalFreq = GetFreq();
570 BenchInfo.UserFreq = GetUserFreq();
571 BenchInfo.GlobalTime = ::GetTimeCount();
572 BenchInfo.UserTime = 0;
573 UserTime.Init();
574}
575
576void CBenchInfoCalc::SetFinishTime(CBenchInfo &dest)
577{
578 dest = BenchInfo;
579 dest.GlobalTime = ::GetTimeCount() - BenchInfo.GlobalTime;
580 dest.UserTime = UserTime.GetUserTime();
581}
582
583class CBenchProgressInfo:
584 public ICompressProgressInfo,
585 public CMyUnknownImp,
586 public CBenchInfoCalc
587{
588public:
589 CBenchProgressStatus *Status;
590 IBenchCallback *Callback;
591
592 CBenchProgressInfo(): Callback(NULL) {}
593 MY_UNKNOWN_IMP
594 STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize);
595};
596
597
598STDMETHODIMP CBenchProgressInfo::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)
599{
600 HRESULT res = Status->GetResult();
601 if (res != S_OK)
602 return res;
603 if (!Callback)
604 return res;
605
606 /*
607 static UInt64 inSizePrev = 0;
608 static UInt64 outSizePrev = 0;
609 UInt64 delta1 = 0, delta2 = 0, val1 = 0, val2 = 0;
610 if (inSize) { val1 = *inSize; delta1 = val1 - inSizePrev; inSizePrev = val1; }
611 if (outSize) { val2 = *outSize; delta2 = val2 - outSizePrev; outSizePrev = val2; }
612 UInt64 percents = delta2 * 1000;
613 if (delta1 != 0)
614 percents /= delta1;
615 printf("=== %7d %7d %7d %7d ratio = %4d\n",
616 (unsigned)(val1 >> 10), (unsigned)(delta1 >> 10),
617 (unsigned)(val2 >> 10), (unsigned)(delta2 >> 10),
618 (unsigned)percents);
619 */
620
621 CBenchInfo info;
622 SetFinishTime(info);
623 if (Status->EncodeMode)
624 {
625 info.UnpackSize = BenchInfo.UnpackSize + *inSize;
626 info.PackSize = BenchInfo.PackSize + *outSize;
627 res = Callback->SetEncodeResult(info, false);
628 }
629 else
630 {
631 info.PackSize = BenchInfo.PackSize + *inSize;
632 info.UnpackSize = BenchInfo.UnpackSize + *outSize;
633 res = Callback->SetDecodeResult(info, false);
634 }
635 if (res != S_OK)
636 Status->SetResult(res);
637 return res;
638}
639
640static const unsigned kSubBits = 8;
641
642static unsigned GetLogSize(UInt64 size)
643{
644 unsigned i = 0;
645 for (;;)
646 {
647 i++; size >>= 1; if (size == 0) break;
648 }
649 return i;
650}
651
652
653static UInt32 GetLogSize_Sub(UInt64 size)
654{
655 if (size <= 1)
656 return 0;
657 const unsigned i = GetLogSize(size) - 1;
658 UInt32 v;
659 if (i <= kSubBits)
660 v = (UInt32)(size) << (kSubBits - i);
661 else
662 v = (UInt32)(size >> (i - kSubBits));
663 return ((UInt32)i << kSubBits) + (v & (((UInt32)1 << kSubBits) - 1));
664}
665
666
667static UInt64 Get_UInt64_from_double(double v)
668{
669 const UInt64 kMaxVal = (UInt64)1 << 62;
670 if (v > (double)(Int64)kMaxVal)
671 return kMaxVal;
672 return (UInt64)v;
673}
674
675static UInt64 MyMultDiv64(UInt64 m1, UInt64 m2, UInt64 d)
676{
677 if (d == 0)
678 d = 1;
679 const double v =
680 (double)(Int64)m1 *
681 (double)(Int64)m2 /
682 (double)(Int64)d;
683 return Get_UInt64_from_double(v);
684 /*
685 unsigned n1 = GetLogSize(m1);
686 unsigned n2 = GetLogSize(m2);
687 while (n1 + n2 > 64)
688 {
689 if (n1 >= n2)
690 {
691 m1 >>= 1;
692 n1--;
693 }
694 else
695 {
696 m2 >>= 1;
697 n2--;
698 }
699 d >>= 1;
700 }
701
702 if (d == 0)
703 d = 1;
704 return m1 * m2 / d;
705 */
706}
707
708
709UInt64 CBenchInfo::GetUsage() const
710{
711 UInt64 userTime = UserTime;
712 UInt64 userFreq = UserFreq;
713 UInt64 globalTime = GlobalTime;
714 UInt64 globalFreq = GlobalFreq;
715
716 if (userFreq == 0)
717 userFreq = 1;
718 if (globalTime == 0)
719 globalTime = 1;
720
721 const double v =
722 ((double)(Int64)userTime / (double)(Int64)userFreq)
723 * ((double)(Int64)globalFreq / (double)(Int64)globalTime)
724 * (double)(Int64)kBenchmarkUsageMult;
725 return Get_UInt64_from_double(v);
726 /*
727 return MyMultDiv64(
728 MyMultDiv64(kBenchmarkUsageMult, userTime, userFreq),
729 globalFreq, globalTime);
730 */
731}
732
733
734UInt64 CBenchInfo::GetRatingPerUsage(UInt64 rating) const
735{
736 if (UserTime == 0)
737 {
738 return 0;
739 // userTime = 1;
740 }
741 UInt64 globalFreq = GlobalFreq;
742 if (globalFreq == 0)
743 globalFreq = 1;
744
745 const double v =
746 ((double)(Int64)GlobalTime / (double)(Int64)globalFreq)
747 * ((double)(Int64)UserFreq / (double)(Int64)UserTime)
748 * (double)(Int64)rating;
749 return Get_UInt64_from_double(v);
750 /*
751 return MyMultDiv64(
752 MyMultDiv64(rating, UserFreq, UserTime),
753 GlobalTime, globalFreq);
754 */
755}
756
757
758UInt64 CBenchInfo::GetSpeed(UInt64 numUnits) const
759{
760 return MyMultDiv64(numUnits, GlobalFreq, GlobalTime);
761}
762
763struct CBenchProps
764{
765 bool LzmaRatingMode;
766
767 UInt32 EncComplex;
768 UInt32 DecComplexCompr;
769 UInt32 DecComplexUnc;
770
771 unsigned KeySize;
772
773 CBenchProps():
774 LzmaRatingMode(false),
775 KeySize(0)
776 {}
777
778 void SetLzmaCompexity();
779
780 UInt64 GeComprCommands(UInt64 unpackSize)
781 {
782 const UInt32 kMinSize = 100;
783 if (unpackSize < kMinSize)
784 unpackSize = kMinSize;
785 return unpackSize * EncComplex;
786 }
787
788 UInt64 GeDecomprCommands(UInt64 packSize, UInt64 unpackSize)
789 {
790 return (packSize * DecComplexCompr + unpackSize * DecComplexUnc);
791 }
792
793 UInt64 GetCompressRating(UInt64 dictSize, UInt64 elapsedTime, UInt64 freq, UInt64 size);
794 UInt64 GetDecompressRating(UInt64 elapsedTime, UInt64 freq, UInt64 outSize, UInt64 inSize, UInt64 numIterations);
795};
796
797void CBenchProps::SetLzmaCompexity()
798{
799 EncComplex = 1200;
800 DecComplexUnc = 4;
801 DecComplexCompr = 190;
802 LzmaRatingMode = true;
803}
804
805UInt64 CBenchProps::GetCompressRating(UInt64 dictSize, UInt64 elapsedTime, UInt64 freq, UInt64 size)
806{
807 if (dictSize < (1 << kBenchMinDicLogSize))
808 dictSize = (1 << kBenchMinDicLogSize);
809 UInt64 encComplex = EncComplex;
810 if (LzmaRatingMode)
811 {
812 /*
813 for (UInt64 uu = 0; uu < (UInt64)0xf << 60;)
814 {
815 unsigned rr = GetLogSize_Sub(uu);
816 printf("\n%16I64x , log = %4x", uu, rr);
817 uu += 1;
818 uu += uu / 50;
819 }
820 */
821 // throw 1;
822 const UInt32 t = GetLogSize_Sub(dictSize) - (kBenchMinDicLogSize << kSubBits);
823 encComplex = 870 + ((t * t * 5) >> (2 * kSubBits));
824 }
825 const UInt64 numCommands = (UInt64)size * encComplex;
826 return MyMultDiv64(numCommands, freq, elapsedTime);
827}
828
829UInt64 CBenchProps::GetDecompressRating(UInt64 elapsedTime, UInt64 freq, UInt64 outSize, UInt64 inSize, UInt64 numIterations)
830{
831 const UInt64 numCommands = (inSize * DecComplexCompr + outSize * DecComplexUnc) * numIterations;
832 return MyMultDiv64(numCommands, freq, elapsedTime);
833}
834
835
836
837UInt64 CBenchInfo::GetRating_LzmaEnc(UInt64 dictSize) const
838{
839 CBenchProps props;
840 props.SetLzmaCompexity();
841 return props.GetCompressRating(dictSize, GlobalTime, GlobalFreq, UnpackSize * NumIterations);
842}
843
844UInt64 CBenchInfo::GetRating_LzmaDec() const
845{
846 CBenchProps props;
847 props.SetLzmaCompexity();
848 return props.GetDecompressRating(GlobalTime, GlobalFreq, UnpackSize, PackSize, NumIterations);
849}
850
851
852#ifndef _7ZIP_ST
853
854#define NUM_CPU_LEVELS_MAX 3
855
856struct CAffinityMode
857{
858 unsigned NumBundleThreads;
859 unsigned NumLevels;
860 unsigned NumCoreThreads;
861 unsigned NumCores;
862 // unsigned DivideNum;
863 UInt32 Sizes[NUM_CPU_LEVELS_MAX];
864
865 void SetLevels(unsigned numCores, unsigned numCoreThreads);
866 DWORD_PTR GetAffinityMask(UInt32 bundleIndex, CCpuSet *cpuSet) const;
867 bool NeedAffinity() const { return NumBundleThreads != 0; }
868
869 WRes CreateThread_WithAffinity(NWindows::CThread &thread, THREAD_FUNC_TYPE startAddress, LPVOID parameter, UInt32 bundleIndex) const
870 {
871 if (NeedAffinity())
872 {
873 CCpuSet cpuSet;
874 GetAffinityMask(bundleIndex, &cpuSet);
875 return thread.Create_With_CpuSet(startAddress, parameter, &cpuSet);
876 }
877 return thread.Create(startAddress, parameter);
878 }
879
880 CAffinityMode():
881 NumBundleThreads(0),
882 NumLevels(0),
883 NumCoreThreads(1)
884 // DivideNum(1)
885 {}
886};
887
888void CAffinityMode::SetLevels(unsigned numCores, unsigned numCoreThreads)
889{
890 NumCores = numCores;
891 NumCoreThreads = numCoreThreads;
892 NumLevels = 0;
893 if (numCoreThreads == 0 || numCores == 0 || numCores % numCoreThreads != 0)
894 return;
895 UInt32 c = numCores / numCoreThreads;
896 UInt32 c2 = 1;
897 while ((c & 1) == 0)
898 {
899 c >>= 1;
900 c2 <<= 1;
901 }
902 if (c2 != 1)
903 Sizes[NumLevels++] = c2;
904 if (c != 1)
905 Sizes[NumLevels++] = c;
906 if (numCoreThreads != 1)
907 Sizes[NumLevels++] = numCoreThreads;
908 if (NumLevels == 0)
909 Sizes[NumLevels++] = 1;
910
911 /*
912 printf("\n Cores:");
913 for (unsigned i = 0; i < NumLevels; i++)
914 {
915 printf(" %d", Sizes[i]);
916 }
917 printf("\n");
918 */
919}
920
921
922DWORD_PTR CAffinityMode::GetAffinityMask(UInt32 bundleIndex, CCpuSet *cpuSet) const
923{
924 CpuSet_Zero(cpuSet);
925
926 if (NumLevels == 0)
927 return 0;
928
929 // printf("\n%2d", bundleIndex);
930
931 /*
932 UInt32 low = 0;
933 if (DivideNum != 1)
934 {
935 low = bundleIndex % DivideNum;
936 bundleIndex /= DivideNum;
937 }
938 */
939
940 UInt32 numGroups = NumCores / NumBundleThreads;
941 UInt32 m = bundleIndex % numGroups;
942 UInt32 v = 0;
943 for (unsigned i = 0; i < NumLevels; i++)
944 {
945 UInt32 size = Sizes[i];
946 while ((size & 1) == 0)
947 {
948 v *= 2;
949 v |= (m & 1);
950 m >>= 1;
951 size >>= 1;
952 }
953 v *= size;
954 v += m % size;
955 m /= size;
956 }
957
958 // UInt32 nb = NumBundleThreads / DivideNum;
959 UInt32 nb = NumBundleThreads;
960
961 DWORD_PTR mask = ((DWORD_PTR)1 << nb) - 1;
962 // v += low;
963 mask <<= v;
964
965 // printf(" %2d %8x \n ", v, (unsigned)mask);
966 #ifdef _WIN32
967 *cpuSet = mask;
968 #else
969 {
970 for (unsigned k = 0; k < nb; k++)
971 CpuSet_Set(cpuSet, v + k);
972 }
973 #endif
974
975 return mask;
976}
977
978
979struct CBenchSyncCommon
980{
981 bool ExitMode;
982 NSynchronization::CManualResetEvent StartEvent;
983
984 CBenchSyncCommon(): ExitMode(false) {}
985};
986
987#endif
988
989
990
991class CEncoderInfo;
992
993class CEncoderInfo
994{
995 CLASS_NO_COPY(CEncoderInfo)
996
997public:
998
999 #ifndef _7ZIP_ST
1000 NWindows::CThread thread[2];
1001 NSynchronization::CManualResetEvent ReadyEvent;
1002 UInt32 NumDecoderSubThreads;
1003 CBenchSyncCommon *Common;
1004 UInt32 EncoderIndex;
1005 UInt32 NumEncoderInternalThreads;
1006 CAffinityMode AffinityMode;
1007 #endif
1008
1009 CMyComPtr<ICompressCoder> _encoder;
1010 CMyComPtr<ICompressFilter> _encoderFilter;
1011 CBenchProgressInfo *progressInfoSpec[2];
1012 CMyComPtr<ICompressProgressInfo> progressInfo[2];
1013 UInt64 NumIterations;
1014
1015 UInt32 Salt;
1016
1017 #ifdef USE_ALLOCA
1018 size_t AllocaSize;
1019 #endif
1020
1021 unsigned KeySize;
1022 Byte _key[32];
1023 Byte _iv[16];
1024
1025 HRESULT Set_Key_and_IV(ICryptoProperties *cp)
1026 {
1027 RINOK(cp->SetKey(_key, KeySize));
1028 return cp->SetInitVector(_iv, sizeof(_iv));
1029 }
1030
1031 Byte _psw[16];
1032
1033 bool CheckCrc_Enc;
1034 bool CheckCrc_Dec;
1035
1036 struct CDecoderInfo
1037 {
1038 CEncoderInfo *Encoder;
1039 UInt32 DecoderIndex;
1040 bool CallbackMode;
1041
1042 #ifdef USE_ALLOCA
1043 size_t AllocaSize;
1044 #endif
1045 };
1046 CDecoderInfo decodersInfo[2];
1047
1048 CMyComPtr<ICompressCoder> _decoders[2];
1049 CMyComPtr<ICompressFilter> _decoderFilter;
1050
1051 HRESULT Results[2];
1052 CBenchmarkOutStream *outStreamSpec;
1053 CMyComPtr<ISequentialOutStream> outStream;
1054 IBenchCallback *callback;
1055 IBenchPrintCallback *printCallback;
1056 UInt32 crc;
1057 size_t kBufferSize;
1058 size_t compressedSize;
1059 const Byte *uncompressedDataPtr;
1060
1061 const Byte *fileData;
1062 CBenchRandomGenerator rg;
1063
1064 CMidAlignedBuffer rgCopy; // it must be 16-byte aligned !!!
1065
1066 // CBenchmarkOutStream *propStreamSpec;
1067 Byte propsData[kMaxMethodPropSize];
1068 CBufPtrSeqOutStream *propStreamSpec;
1069 CMyComPtr<ISequentialOutStream> propStream;
1070
1071 unsigned generateDictBits;
1072 COneMethodInfo _method;
1073
1074 // for decode
1075 size_t _uncompressedDataSize;
1076
1077 HRESULT Generate();
1078 HRESULT Encode();
1079 HRESULT Decode(UInt32 decoderIndex);
1080
1081 CEncoderInfo():
1082 #ifndef _7ZIP_ST
1083 Common(NULL),
1084 #endif
1085 Salt(0),
1086 KeySize(0),
1087 CheckCrc_Enc(true),
1088 CheckCrc_Dec(true),
1089 outStreamSpec(NULL),
1090 callback(NULL),
1091 printCallback(NULL),
1092 fileData(NULL),
1093 propStreamSpec(NULL)
1094 {}
1095
1096 #ifndef _7ZIP_ST
1097
1098 static THREAD_FUNC_DECL EncodeThreadFunction(void *param)
1099 {
1100 HRESULT res;
1101 CEncoderInfo *encoder = (CEncoderInfo *)param;
1102 try
1103 {
1104 #ifdef USE_ALLOCA
1105 alloca(encoder->AllocaSize);
1106 #endif
1107
1108 res = encoder->Encode();
1109 }
1110 catch(...)
1111 {
1112 res = E_FAIL;
1113 }
1114 encoder->Results[0] = res;
1115 if (res != S_OK)
1116 encoder->progressInfoSpec[0]->Status->SetResult(res);
1117 encoder->ReadyEvent.Set();
1118 return 0;
1119 }
1120
1121 static THREAD_FUNC_DECL DecodeThreadFunction(void *param)
1122 {
1123 CDecoderInfo *decoder = (CDecoderInfo *)param;
1124
1125 #ifdef USE_ALLOCA
1126 alloca(decoder->AllocaSize);
1127 #endif
1128
1129 CEncoderInfo *encoder = decoder->Encoder;
1130 encoder->Results[decoder->DecoderIndex] = encoder->Decode(decoder->DecoderIndex);
1131 return 0;
1132 }
1133
1134 HRESULT CreateEncoderThread()
1135 {
1136 WRes res = 0;
1137 if (!ReadyEvent.IsCreated())
1138 res = ReadyEvent.Create();
1139 if (res == 0)
1140 res = AffinityMode.CreateThread_WithAffinity(thread[0], EncodeThreadFunction, this,
1141 EncoderIndex);
1142 return HRESULT_FROM_WIN32(res);
1143 }
1144
1145 HRESULT CreateDecoderThread(unsigned index, bool callbackMode
1146 #ifdef USE_ALLOCA
1147 , size_t allocaSize
1148 #endif
1149 )
1150 {
1151 CDecoderInfo &decoder = decodersInfo[index];
1152 decoder.DecoderIndex = index;
1153 decoder.Encoder = this;
1154
1155 #ifdef USE_ALLOCA
1156 decoder.AllocaSize = allocaSize;
1157 #endif
1158
1159 decoder.CallbackMode = callbackMode;
1160
1161 WRes res = AffinityMode.CreateThread_WithAffinity(thread[index], DecodeThreadFunction, &decoder,
1162 // EncoderIndex * NumEncoderInternalThreads + index
1163 EncoderIndex
1164 );
1165
1166 return HRESULT_FROM_WIN32(res);
1167 }
1168
1169 #endif
1170};
1171
1172
1173
1174
1175static size_t GetBenchCompressedSize(size_t bufferSize)
1176{
1177 return kCompressedAdditionalSize + bufferSize + bufferSize / 16;
1178 // kBufferSize / 2;
1179}
1180
1181
1182HRESULT CEncoderInfo::Generate()
1183{
1184 const COneMethodInfo &method = _method;
1185
1186 // we need extra space, if input data is already compressed
1187 const size_t kCompressedBufferSize = GetBenchCompressedSize(kBufferSize);
1188
1189 if (kCompressedBufferSize < kBufferSize)
1190 return E_FAIL;
1191
1192 uncompressedDataPtr = fileData;
1193
1194 if (!fileData)
1195 {
1196 ALLOC_WITH_HRESULT(&rg, kBufferSize);
1197
1198 // DWORD ttt = GetTickCount();
1199 if (generateDictBits == 0)
1200 rg.GenerateSimpleRandom(Salt);
1201 else
1202 {
1203 if (generateDictBits >= sizeof(size_t) * 8
1204 && kBufferSize > ((size_t)1 << (sizeof(size_t) * 8 - 1)))
1205 return E_INVALIDARG;
1206 rg.GenerateLz(generateDictBits, Salt);
1207 // return E_ABORT; // for debug
1208 }
1209 // printf("\n%d\n ", GetTickCount() - ttt);
1210
1211 crc = CrcCalc((const Byte *)rg, rg.Size());
1212 uncompressedDataPtr = (const Byte *)rg;
1213 }
1214
1215 if (_encoderFilter)
1216 {
1217 ALLOC_WITH_HRESULT(&rgCopy, kBufferSize);
1218 }
1219
1220
1221 if (!outStream)
1222 {
1223 outStreamSpec = new CBenchmarkOutStream;
1224 outStream = outStreamSpec;
1225 }
1226
1227 ALLOC_WITH_HRESULT(outStreamSpec, kCompressedBufferSize)
1228
1229 if (!propStream)
1230 {
1231 propStreamSpec = new CBufPtrSeqOutStream; // CBenchmarkOutStream;
1232 propStream = propStreamSpec;
1233 }
1234 // ALLOC_WITH_HRESULT_2(propStreamSpec, kMaxMethodPropSize);
1235 // propStreamSpec->Init(true, false);
1236 propStreamSpec->Init(propsData, sizeof(propsData));
1237
1238
1239 CMyComPtr<IUnknown> coder;
1240 if (_encoderFilter)
1241 coder = _encoderFilter;
1242 else
1243 coder = _encoder;
1244 {
1245 CMyComPtr<ICompressSetCoderProperties> scp;
1246 coder.QueryInterface(IID_ICompressSetCoderProperties, &scp);
1247 if (scp)
1248 {
1249 const UInt64 reduceSize = kBufferSize;
1250
1251 /* in posix new thread uses same affinity as parent thread,
1252 so we don't need to send affinity to coder in posix */
1253 UInt64 affMask;
1254 #if !defined(_7ZIP_ST) && defined(_WIN32)
1255 {
1256 CCpuSet cpuSet;
1257 affMask = AffinityMode.GetAffinityMask(EncoderIndex, &cpuSet);
1258 }
1259 #else
1260 affMask = 0;
1261 #endif
1262 // affMask <<= 3; // debug line: to test no affinity in coder;
1263 // affMask = 0;
1264
1265 RINOK(method.SetCoderProps_DSReduce_Aff(scp, &reduceSize, (affMask != 0 ? &affMask : NULL)));
1266 }
1267 else
1268 {
1269 if (method.AreThereNonOptionalProps())
1270 return E_INVALIDARG;
1271 }
1272
1273 CMyComPtr<ICompressWriteCoderProperties> writeCoderProps;
1274 coder.QueryInterface(IID_ICompressWriteCoderProperties, &writeCoderProps);
1275 if (writeCoderProps)
1276 {
1277 RINOK(writeCoderProps->WriteCoderProperties(propStream));
1278 }
1279
1280 {
1281 CMyComPtr<ICryptoSetPassword> sp;
1282 coder.QueryInterface(IID_ICryptoSetPassword, &sp);
1283 if (sp)
1284 {
1285 RINOK(sp->CryptoSetPassword(_psw, sizeof(_psw)));
1286
1287 // we must call encoding one time to calculate password key for key cache.
1288 // it must be after WriteCoderProperties!
1289 Byte temp[16];
1290 memset(temp, 0, sizeof(temp));
1291
1292 if (_encoderFilter)
1293 {
1294 _encoderFilter->Init();
1295 _encoderFilter->Filter(temp, sizeof(temp));
1296 }
1297 else
1298 {
1299 CBenchmarkInStream *inStreamSpec = new CBenchmarkInStream;
1300 CMyComPtr<ISequentialInStream> inStream = inStreamSpec;
1301 inStreamSpec->Init(temp, sizeof(temp));
1302
1303 CCrcOutStream *crcStreamSpec = new CCrcOutStream;
1304 CMyComPtr<ISequentialOutStream> crcStream = crcStreamSpec;
1305 crcStreamSpec->Init();
1306
1307 RINOK(_encoder->Code(inStream, crcStream, 0, 0, NULL));
1308 }
1309 }
1310 }
1311 }
1312
1313 return S_OK;
1314}
1315
1316
1317static void My_FilterBench(ICompressFilter *filter, Byte *data, size_t size)
1318{
1319 while (size != 0)
1320 {
1321 UInt32 cur = (UInt32)1 << 31;
1322 if (cur > size)
1323 cur = (UInt32)size;
1324 UInt32 processed = filter->Filter(data, cur);
1325 data += processed;
1326 // if (processed > size) (in AES filter), we must fill last block with zeros.
1327 // but it is not important for benchmark. So we just copy that data without filtering.
1328 if (processed > size || processed == 0)
1329 break;
1330 size -= processed;
1331 }
1332}
1333
1334
1335HRESULT CEncoderInfo::Encode()
1336{
1337 // printf("\nCEncoderInfo::Generate\n");
1338
1339 RINOK(Generate());
1340
1341 // printf("\n2222\n");
1342
1343 #ifndef _7ZIP_ST
1344 if (Common)
1345 {
1346 Results[0] = S_OK;
1347 WRes wres = ReadyEvent.Set();
1348 if (wres == 0)
1349 wres = Common->StartEvent.Lock();
1350 if (wres != 0)
1351 return HRESULT_FROM_WIN32(wres);
1352 if (Common->ExitMode)
1353 return S_OK;
1354 }
1355 else
1356 #endif
1357 {
1358 CBenchProgressInfo *bpi = progressInfoSpec[0];
1359 bpi->SetStartTime();
1360 }
1361
1362
1363 CBenchInfo &bi = progressInfoSpec[0]->BenchInfo;
1364 bi.UnpackSize = 0;
1365 bi.PackSize = 0;
1366 CMyComPtr<ICryptoProperties> cp;
1367 CMyComPtr<IUnknown> coder;
1368 if (_encoderFilter)
1369 coder = _encoderFilter;
1370 else
1371 coder = _encoder;
1372 coder.QueryInterface(IID_ICryptoProperties, &cp);
1373 CBenchmarkInStream *inStreamSpec = new CBenchmarkInStream;
1374 CMyComPtr<ISequentialInStream> inStream = inStreamSpec;
1375
1376 if (cp)
1377 {
1378 RINOK(Set_Key_and_IV(cp));
1379 }
1380
1381 compressedSize = 0;
1382 if (_encoderFilter)
1383 compressedSize = kBufferSize;
1384
1385 // CBenchmarkOutStream *outStreamSpec = this->outStreamSpec;
1386 UInt64 prev = 0;
1387 const UInt32 mask = (CheckCrc_Enc ? 0 : 0xFFF);
1388 bool useCrc = (mask < NumIterations);
1389 bool crcPrev_defined = false;
1390 UInt32 crcPrev = 0;
1391 UInt64 i = NumIterations;
1392 // printCallback->NewLine();
1393
1394 while (i != 0)
1395 {
1396 i--;
1397 if (printCallback && bi.UnpackSize - prev >= (1 << 24))
1398 {
1399 RINOK(printCallback->CheckBreak());
1400 prev = bi.UnpackSize;
1401 }
1402
1403 /*
1404 CBenchInfo info;
1405 progressInfoSpec[0]->SetStartTime();
1406 */
1407
1408 bool calcCrc = false;
1409 if (useCrc)
1410 calcCrc = (((UInt32)i & mask) == 0);
1411
1412 if (_encoderFilter)
1413 {
1414 // if (needRealData)
1415 memcpy((Byte *)*outStreamSpec, uncompressedDataPtr, kBufferSize);
1416 _encoderFilter->Init();
1417 My_FilterBench(_encoderFilter, (Byte *)*outStreamSpec, kBufferSize);
1418 if (calcCrc)
1419 {
1420 outStreamSpec->InitCrc();
1421 outStreamSpec->Calc((Byte *)*outStreamSpec, kBufferSize);
1422 }
1423 }
1424 else
1425 {
1426 outStreamSpec->Init(true, calcCrc); // write real data for speed consistency at any number of iterations
1427 inStreamSpec->Init(uncompressedDataPtr, kBufferSize);
1428 RINOK(_encoder->Code(inStream, outStream, NULL, NULL, progressInfo[0]));
1429 if (!inStreamSpec->WasFinished())
1430 return E_FAIL;
1431 if (compressedSize != outStreamSpec->Pos)
1432 {
1433 if (compressedSize != 0)
1434 return E_FAIL;
1435 compressedSize = outStreamSpec->Pos;
1436 }
1437 }
1438
1439 // outStreamSpec->Print();
1440
1441 if (calcCrc)
1442 {
1443 const UInt32 crc2 = CRC_GET_DIGEST(outStreamSpec->Crc);
1444 if (crcPrev_defined && crcPrev != crc2)
1445 return E_FAIL;
1446 crcPrev = crc2;
1447 crcPrev_defined = true;
1448 }
1449
1450 bi.UnpackSize += kBufferSize;
1451 bi.PackSize += compressedSize;
1452
1453 /*
1454 {
1455 progressInfoSpec[0]->SetFinishTime(info);
1456 info.UnpackSize = 0;
1457 info.PackSize = 0;
1458 info.NumIterations = 1;
1459
1460 info.UnpackSize = kBufferSize;
1461 info.PackSize = compressedSize;
1462 // printf("\n%7d\n", encoder.compressedSize);
1463
1464 RINOK(callback->SetEncodeResult(info, true));
1465 printCallback->NewLine();
1466 }
1467 */
1468
1469 }
1470
1471 _encoder.Release();
1472 _encoderFilter.Release();
1473 return S_OK;
1474}
1475
1476
1477HRESULT CEncoderInfo::Decode(UInt32 decoderIndex)
1478{
1479 CBenchmarkInStream *inStreamSpec = new CBenchmarkInStream;
1480 CMyComPtr<ISequentialInStream> inStream = inStreamSpec;
1481 CMyComPtr<ICompressCoder> &decoder = _decoders[decoderIndex];
1482 CMyComPtr<IUnknown> coder;
1483 if (_decoderFilter)
1484 {
1485 if (decoderIndex != 0)
1486 return E_FAIL;
1487 coder = _decoderFilter;
1488 }
1489 else
1490 coder = decoder;
1491
1492 CMyComPtr<ICompressSetDecoderProperties2> setDecProps;
1493 coder.QueryInterface(IID_ICompressSetDecoderProperties2, &setDecProps);
1494 if (!setDecProps && propStreamSpec->GetPos() != 0)
1495 return E_FAIL;
1496
1497 CCrcOutStream *crcOutStreamSpec = new CCrcOutStream;
1498 CMyComPtr<ISequentialOutStream> crcOutStream = crcOutStreamSpec;
1499
1500 CBenchProgressInfo *pi = progressInfoSpec[decoderIndex];
1501 pi->BenchInfo.UnpackSize = 0;
1502 pi->BenchInfo.PackSize = 0;
1503
1504 #ifndef _7ZIP_ST
1505 {
1506 CMyComPtr<ICompressSetCoderMt> setCoderMt;
1507 coder.QueryInterface(IID_ICompressSetCoderMt, &setCoderMt);
1508 if (setCoderMt)
1509 {
1510 RINOK(setCoderMt->SetNumberOfThreads(NumDecoderSubThreads));
1511 }
1512 }
1513 #endif
1514
1515 CMyComPtr<ICompressSetCoderProperties> scp;
1516 coder.QueryInterface(IID_ICompressSetCoderProperties, &scp);
1517 if (scp)
1518 {
1519 const UInt64 reduceSize = _uncompressedDataSize;
1520 RINOK(_method.SetCoderProps(scp, &reduceSize));
1521 }
1522
1523 CMyComPtr<ICryptoProperties> cp;
1524 coder.QueryInterface(IID_ICryptoProperties, &cp);
1525
1526 if (setDecProps)
1527 {
1528 RINOK(setDecProps->SetDecoderProperties2(
1529 /* (const Byte *)*propStreamSpec, */
1530 propsData,
1531 (UInt32)propStreamSpec->GetPos()));
1532 }
1533
1534 {
1535 CMyComPtr<ICryptoSetPassword> sp;
1536 coder.QueryInterface(IID_ICryptoSetPassword, &sp);
1537 if (sp)
1538 {
1539 RINOK(sp->CryptoSetPassword(_psw, sizeof(_psw)));
1540 }
1541 }
1542
1543 UInt64 prev = 0;
1544
1545 if (cp)
1546 {
1547 RINOK(Set_Key_and_IV(cp));
1548 }
1549
1550 CMyComPtr<ICompressSetFinishMode> setFinishMode;
1551
1552 if (_decoderFilter)
1553 {
1554 if (compressedSize > rgCopy.Size())
1555 return E_FAIL;
1556 }
1557 else
1558 {
1559 decoder->QueryInterface(IID_ICompressSetFinishMode, (void **)&setFinishMode);
1560 }
1561
1562 const UInt64 numIterations = this->NumIterations;
1563 const UInt32 mask = (CheckCrc_Dec ? 0 : 0xFFF);
1564
1565 for (UInt64 i = 0; i < numIterations; i++)
1566 {
1567 if (printCallback && pi->BenchInfo.UnpackSize - prev >= (1 << 24))
1568 {
1569 RINOK(printCallback->CheckBreak());
1570 prev = pi->BenchInfo.UnpackSize;
1571 }
1572
1573 const UInt64 outSize = kBufferSize;
1574 bool calcCrc = false;
1575 if (((UInt32)i & mask) == 0)
1576 calcCrc = true;
1577 crcOutStreamSpec->Init();
1578
1579 if (_decoderFilter)
1580 {
1581 if (calcCrc) // for pure filter speed test without multi-iteration consistency
1582 // if (needRealData)
1583 memcpy((Byte *)rgCopy, (const Byte *)*outStreamSpec, compressedSize);
1584 _decoderFilter->Init();
1585 My_FilterBench(_decoderFilter, (Byte *)rgCopy, compressedSize);
1586 if (calcCrc)
1587 crcOutStreamSpec->Calc((const Byte *)rgCopy, compressedSize);
1588 }
1589 else
1590 {
1591 crcOutStreamSpec->CalcCrc = calcCrc;
1592 inStreamSpec->Init((const Byte *)*outStreamSpec, compressedSize);
1593
1594 if (setFinishMode)
1595 {
1596 RINOK(setFinishMode->SetFinishMode(BoolToUInt(true)));
1597 }
1598
1599 RINOK(decoder->Code(inStream, crcOutStream, 0, &outSize, progressInfo[decoderIndex]));
1600
1601 if (setFinishMode)
1602 {
1603 if (!inStreamSpec->WasFinished())
1604 return S_FALSE;
1605
1606 CMyComPtr<ICompressGetInStreamProcessedSize> getInStreamProcessedSize;
1607 decoder.QueryInterface(IID_ICompressGetInStreamProcessedSize, (void **)&getInStreamProcessedSize);
1608
1609 if (getInStreamProcessedSize)
1610 {
1611 UInt64 processed;
1612 RINOK(getInStreamProcessedSize->GetInStreamProcessedSize(&processed));
1613 if (processed != compressedSize)
1614 return S_FALSE;
1615 }
1616 }
1617
1618 if (crcOutStreamSpec->Pos != outSize)
1619 return S_FALSE;
1620 }
1621
1622 if (calcCrc && CRC_GET_DIGEST(crcOutStreamSpec->Crc) != crc)
1623 return S_FALSE;
1624
1625 pi->BenchInfo.UnpackSize += kBufferSize;
1626 pi->BenchInfo.PackSize += compressedSize;
1627 }
1628
1629 decoder.Release();
1630 _decoderFilter.Release();
1631 return S_OK;
1632}
1633
1634
1635static const UInt32 kNumThreadsMax = (1 << 12);
1636
1637struct CBenchEncoders
1638{
1639 CEncoderInfo *encoders;
1640 CBenchEncoders(UInt32 num): encoders(NULL) { encoders = new CEncoderInfo[num]; }
1641 ~CBenchEncoders() { delete []encoders; }
1642};
1643
1644
1645static UInt64 GetNumIterations(UInt64 numCommands, UInt64 complexInCommands)
1646{
1647 if (numCommands < (1 << 4))
1648 numCommands = (1 << 4);
1649 UInt64 res = complexInCommands / numCommands;
1650 return (res == 0 ? 1 : res);
1651}
1652
1653
1654
1655#ifndef _7ZIP_ST
1656
1657// ---------- CBenchThreadsFlusher ----------
1658
1659struct CBenchThreadsFlusher
1660{
1661 CBenchEncoders *EncodersSpec;
1662 CBenchSyncCommon Common;
1663 unsigned NumThreads;
1664 bool NeedClose;
1665
1666 CBenchThreadsFlusher(): NumThreads(0), NeedClose(false) {}
1667
1668 ~CBenchThreadsFlusher()
1669 {
1670 StartAndWait(true);
1671 }
1672
1673 WRes StartAndWait(bool exitMode = false);
1674};
1675
1676
1677WRes CBenchThreadsFlusher::StartAndWait(bool exitMode)
1678{
1679 if (!NeedClose)
1680 return 0;
1681
1682 Common.ExitMode = exitMode;
1683 WRes res = Common.StartEvent.Set();
1684
1685 for (unsigned i = 0; i < NumThreads; i++)
1686 {
1687 NWindows::CThread &t = EncodersSpec->encoders[i].thread[0];
1688 if (t.IsCreated())
1689 {
1690 WRes res2 = t.Wait_Close();
1691 if (res == 0)
1692 res = res2;
1693 }
1694 }
1695 NeedClose = false;
1696 return res;
1697}
1698
1699#endif // _7ZIP_ST
1700
1701
1702
1703static void SetPseudoRand(Byte *data, size_t size, UInt32 startValue)
1704{
1705 for (size_t i = 0; i < size; i++)
1706 {
1707 data[i] = (Byte)startValue;
1708 startValue++;
1709 }
1710}
1711
1712
1713
1714static HRESULT MethodBench(
1715 DECL_EXTERNAL_CODECS_LOC_VARS
1716 UInt64 complexInCommands,
1717 #ifndef _7ZIP_ST
1718 bool oldLzmaBenchMode,
1719 UInt32 numThreads,
1720 const CAffinityMode *affinityMode,
1721 #endif
1722 const COneMethodInfo &method2,
1723 size_t uncompressedDataSize,
1724 const Byte *fileData,
1725 unsigned generateDictBits,
1726
1727 IBenchPrintCallback *printCallback,
1728 IBenchCallback *callback,
1729 CBenchProps *benchProps)
1730{
1731 COneMethodInfo method = method2;
1732 UInt64 methodId;
1733 UInt32 numStreams;
1734 const int codecIndex = FindMethod_Index(
1735 EXTERNAL_CODECS_LOC_VARS
1736 method.MethodName, true,
1737 methodId, numStreams);
1738 if (codecIndex < 0)
1739 return E_NOTIMPL;
1740 if (numStreams != 1)
1741 return E_INVALIDARG;
1742
1743 UInt32 numEncoderThreads = 1;
1744 UInt32 numSubDecoderThreads = 1;
1745
1746 #ifndef _7ZIP_ST
1747 numEncoderThreads = numThreads;
1748
1749 if (oldLzmaBenchMode)
1750 if (methodId == k_LZMA)
1751 {
1752 if (numThreads == 1 && method.Get_NumThreads() < 0)
1753 method.AddProp_NumThreads(1);
1754 const UInt32 numLzmaThreads = method.Get_Lzma_NumThreads();
1755 if (numThreads > 1 && numLzmaThreads > 1)
1756 {
1757 numEncoderThreads = (numThreads + 1) / 2; // 20.03
1758 numSubDecoderThreads = 2;
1759 }
1760 }
1761
1762 bool mtEncMode = (numEncoderThreads > 1) || affinityMode->NeedAffinity();
1763
1764 #endif
1765
1766 CBenchEncoders encodersSpec(numEncoderThreads);
1767 CEncoderInfo *encoders = encodersSpec.encoders;
1768
1769 UInt32 i;
1770
1771 for (i = 0; i < numEncoderThreads; i++)
1772 {
1773 CEncoderInfo &encoder = encoders[i];
1774 encoder.callback = (i == 0) ? callback : 0;
1775 encoder.printCallback = printCallback;
1776
1777 #ifndef _7ZIP_ST
1778 encoder.EncoderIndex = i;
1779 encoder.NumEncoderInternalThreads = numSubDecoderThreads;
1780 encoder.AffinityMode = *affinityMode;
1781
1782 /*
1783 if (numSubDecoderThreads > 1)
1784 if (encoder.AffinityMode.NeedAffinity()
1785 && encoder.AffinityMode.NumBundleThreads == 1)
1786 {
1787 // if old LZMA benchmark uses two threads in coder, we increase (NumBundleThreads) for old LZMA benchmark uses two threads instead of one
1788 if (encoder.AffinityMode.NumBundleThreads * 2 <= encoder.AffinityMode.NumCores)
1789 encoder.AffinityMode.NumBundleThreads *= 2;
1790 }
1791 */
1792
1793 #endif
1794
1795 {
1796 CCreatedCoder cod;
1797 RINOK(CreateCoder_Index(EXTERNAL_CODECS_LOC_VARS (unsigned)codecIndex, true, encoder._encoderFilter, cod));
1798 encoder._encoder = cod.Coder;
1799 if (!encoder._encoder && !encoder._encoderFilter)
1800 return E_NOTIMPL;
1801 }
1802
1803 encoder.CheckCrc_Enc = (benchProps->EncComplex) > 30;
1804 encoder.CheckCrc_Dec = (benchProps->DecComplexCompr + benchProps->DecComplexUnc) > 30;
1805
1806 SetPseudoRand(encoder._iv, sizeof(encoder._iv), 17);
1807 SetPseudoRand(encoder._key, sizeof(encoder._key), 51);
1808 SetPseudoRand(encoder._psw, sizeof(encoder._psw), 123);
1809
1810 for (UInt32 j = 0; j < numSubDecoderThreads; j++)
1811 {
1812 CCreatedCoder cod;
1813 CMyComPtr<ICompressCoder> &decoder = encoder._decoders[j];
1814 RINOK(CreateCoder_Id(EXTERNAL_CODECS_LOC_VARS methodId, false, encoder._decoderFilter, cod));
1815 decoder = cod.Coder;
1816 if (!encoder._decoderFilter && !decoder)
1817 return E_NOTIMPL;
1818 }
1819 }
1820
1821 UInt32 crc = 0;
1822 if (fileData)
1823 crc = CrcCalc(fileData, uncompressedDataSize);
1824
1825 for (i = 0; i < numEncoderThreads; i++)
1826 {
1827 CEncoderInfo &encoder = encoders[i];
1828 encoder._method = method;
1829 encoder.generateDictBits = generateDictBits;
1830 encoder._uncompressedDataSize = uncompressedDataSize;
1831 encoder.kBufferSize = uncompressedDataSize;
1832 encoder.fileData = fileData;
1833 encoder.crc = crc;
1834 }
1835
1836 CBenchProgressStatus status;
1837 status.Res = S_OK;
1838 status.EncodeMode = true;
1839
1840 #ifndef _7ZIP_ST
1841 CBenchThreadsFlusher encoderFlusher;
1842 if (mtEncMode)
1843 {
1844 WRes wres = encoderFlusher.Common.StartEvent.Create();
1845 if (wres != 0)
1846 return HRESULT_FROM_WIN32(wres);
1847 encoderFlusher.NumThreads = numEncoderThreads;
1848 encoderFlusher.EncodersSpec = &encodersSpec;
1849 encoderFlusher.NeedClose = true;
1850 }
1851 #endif
1852
1853 for (i = 0; i < numEncoderThreads; i++)
1854 {
1855 CEncoderInfo &encoder = encoders[i];
1856 encoder.NumIterations = GetNumIterations(benchProps->GeComprCommands(uncompressedDataSize), complexInCommands);
1857 // encoder.NumIterations = 3;
1858 encoder.Salt = g_CrcTable[i & 0xFF];
1859 encoder.Salt ^= (g_CrcTable[(i >> 8) & 0xFF] << 3);
1860 // (g_CrcTable[0] == 0), and (encoder.Salt == 0) for first thread
1861 // printf(" %8x", encoder.Salt);
1862
1863 encoder.KeySize = benchProps->KeySize;
1864
1865 for (int j = 0; j < 2; j++)
1866 {
1867 CBenchProgressInfo *spec = new CBenchProgressInfo;
1868 encoder.progressInfoSpec[j] = spec;
1869 encoder.progressInfo[j] = spec;
1870 spec->Status = &status;
1871 }
1872
1873 if (i == 0)
1874 {
1875 CBenchProgressInfo *bpi = encoder.progressInfoSpec[0];
1876 bpi->Callback = callback;
1877 bpi->BenchInfo.NumIterations = numEncoderThreads;
1878 }
1879
1880 #ifndef _7ZIP_ST
1881 if (mtEncMode)
1882 {
1883 #ifdef USE_ALLOCA
1884 encoder.AllocaSize = (i * 16 * 21) & 0x7FF;
1885 #endif
1886
1887 encoder.Common = &encoderFlusher.Common;
1888 RINOK(encoder.CreateEncoderThread())
1889 }
1890 #endif
1891 }
1892
1893 if (printCallback)
1894 {
1895 RINOK(printCallback->CheckBreak());
1896 }
1897
1898 #ifndef _7ZIP_ST
1899 if (mtEncMode)
1900 {
1901 for (i = 0; i < numEncoderThreads; i++)
1902 {
1903 CEncoderInfo &encoder = encoders[i];
1904 WRes wres = encoder.ReadyEvent.Lock();
1905 if (wres != 0)
1906 return HRESULT_FROM_WIN32(wres);
1907 RINOK(encoder.Results[0]);
1908 }
1909
1910 CBenchProgressInfo *bpi = encoders[0].progressInfoSpec[0];
1911 bpi->SetStartTime();
1912
1913 WRes wres = encoderFlusher.StartAndWait();
1914 if (status.Res == 0 && wres != 0)
1915 return HRESULT_FROM_WIN32(wres);
1916 }
1917 else
1918 #endif
1919 {
1920 RINOK(encoders[0].Encode());
1921 }
1922
1923 RINOK(status.Res);
1924
1925 CBenchInfo info;
1926
1927 encoders[0].progressInfoSpec[0]->SetFinishTime(info);
1928 info.UnpackSize = 0;
1929 info.PackSize = 0;
1930 info.NumIterations = encoders[0].NumIterations;
1931
1932 for (i = 0; i < numEncoderThreads; i++)
1933 {
1934 CEncoderInfo &encoder = encoders[i];
1935 info.UnpackSize += encoder.kBufferSize;
1936 info.PackSize += encoder.compressedSize;
1937 // printf("\n%7d\n", encoder.compressedSize);
1938 }
1939
1940 RINOK(callback->SetEncodeResult(info, true));
1941
1942
1943
1944
1945 // ---------- Decode ----------
1946
1947 status.Res = S_OK;
1948 status.EncodeMode = false;
1949
1950 const UInt32 numDecoderThreads = numEncoderThreads * numSubDecoderThreads;
1951 #ifndef _7ZIP_ST
1952 const bool mtDecoderMode = (numDecoderThreads > 1) || affinityMode->NeedAffinity();
1953 #endif
1954
1955 for (i = 0; i < numEncoderThreads; i++)
1956 {
1957 CEncoderInfo &encoder = encoders[i];
1958
1959 /*
1960 #ifndef _7ZIP_ST
1961 // encoder.affinityMode = *affinityMode;
1962 if (encoder.NumEncoderInternalThreads != 1)
1963 encoder.AffinityMode.DivideNum = encoder.NumEncoderInternalThreads;
1964 #endif
1965 */
1966
1967
1968 if (i == 0)
1969 {
1970 encoder.NumIterations = GetNumIterations(benchProps->GeDecomprCommands(encoder.compressedSize, encoder.kBufferSize), complexInCommands);
1971 CBenchProgressInfo *bpi = encoder.progressInfoSpec[0];
1972 bpi->Callback = callback;
1973 bpi->BenchInfo.NumIterations = numDecoderThreads;
1974 bpi->SetStartTime();
1975 }
1976 else
1977 encoder.NumIterations = encoders[0].NumIterations;
1978
1979 #ifndef _7ZIP_ST
1980 {
1981 int numSubThreads = method.Get_NumThreads();
1982 encoder.NumDecoderSubThreads = (numSubThreads <= 0) ? 1 : (unsigned)numSubThreads;
1983 }
1984 if (mtDecoderMode)
1985 {
1986 for (UInt32 j = 0; j < numSubDecoderThreads; j++)
1987 {
1988 HRESULT res = encoder.CreateDecoderThread(j, (i == 0 && j == 0)
1989 #ifdef USE_ALLOCA
1990 , ((i * numSubDecoderThreads + j) * 16 * 21) & 0x7FF
1991 #endif
1992 );
1993 RINOK(res);
1994 }
1995 }
1996 else
1997 #endif
1998 {
1999 RINOK(encoder.Decode(0));
2000 }
2001 }
2002
2003 #ifndef _7ZIP_ST
2004 if (mtDecoderMode)
2005 {
2006 WRes wres = 0;
2007 HRESULT res = S_OK;
2008 for (i = 0; i < numEncoderThreads; i++)
2009 for (UInt32 j = 0; j < numSubDecoderThreads; j++)
2010 {
2011 CEncoderInfo &encoder = encoders[i];
2012 WRes wres2 = encoder.thread[j].
2013 // Wait(); // later we can get thread times from thread in UNDER_CE
2014 Wait_Close();
2015 if (wres == 0 && wres2 != 0)
2016 wres = wres2;
2017 HRESULT res2 = encoder.Results[j];
2018 if (res == 0 && res2 != 0)
2019 res = res2;
2020 }
2021 if (wres != 0)
2022 return HRESULT_FROM_WIN32(wres);
2023 RINOK(res);
2024 }
2025 #endif // _7ZIP_ST
2026
2027 RINOK(status.Res);
2028 encoders[0].progressInfoSpec[0]->SetFinishTime(info);
2029
2030 /*
2031 #ifndef _7ZIP_ST
2032 #ifdef UNDER_CE
2033 if (mtDecoderMode)
2034 for (i = 0; i < numEncoderThreads; i++)
2035 for (UInt32 j = 0; j < numSubDecoderThreads; j++)
2036 {
2037 FILETIME creationTime, exitTime, kernelTime, userTime;
2038 if (::GetThreadTimes(encoders[i].thread[j], &creationTime, &exitTime, &kernelTime, &userTime) != 0)
2039 info.UserTime += GetTime64(userTime) + GetTime64(kernelTime);
2040 }
2041 #endif
2042 #endif
2043 */
2044
2045 info.UnpackSize = 0;
2046 info.PackSize = 0;
2047 info.NumIterations = numSubDecoderThreads * encoders[0].NumIterations;
2048
2049 for (i = 0; i < numEncoderThreads; i++)
2050 {
2051 CEncoderInfo &encoder = encoders[i];
2052 info.UnpackSize += encoder.kBufferSize;
2053 info.PackSize += encoder.compressedSize;
2054 }
2055
2056 // RINOK(callback->SetDecodeResult(info, false)); // why we called before 21.03 ??
2057 RINOK(callback->SetDecodeResult(info, true));
2058
2059 return S_OK;
2060}
2061
2062
2063
2064static inline UInt64 GetDictSizeFromLog(unsigned dictSizeLog)
2065{
2066 /*
2067 if (dictSizeLog < 32)
2068 return (UInt32)1 << dictSizeLog;
2069 else
2070 return (UInt32)(Int32)-1;
2071 */
2072 return (UInt64)1 << dictSizeLog;
2073}
2074
2075
2076// it's limit of current LZMA implementation that can be changed later
2077#define kLzmaMaxDictSize ((UInt32)15 << 28)
2078
2079static inline UInt64 GetLZMAUsage(bool multiThread, int btMode, UInt64 dict)
2080{
2081 if (dict == 0)
2082 dict = 1;
2083 if (dict > kLzmaMaxDictSize)
2084 dict = kLzmaMaxDictSize;
2085 UInt32 hs = (UInt32)dict - 1;
2086 hs |= (hs >> 1);
2087 hs |= (hs >> 2);
2088 hs |= (hs >> 4);
2089 hs |= (hs >> 8);
2090 hs >>= 1;
2091 hs |= 0xFFFF;
2092 if (hs > (1 << 24))
2093 hs >>= 1;
2094 hs++;
2095 hs += (1 << 16);
2096
2097 const UInt32 kBlockSizeMax = (UInt32)0 - (UInt32)(1 << 16);
2098 UInt64 blockSize = (UInt64)dict + (1 << 16)
2099 + (multiThread ? (1 << 20) : 0);
2100 blockSize += (blockSize >> (blockSize < ((UInt32)1 << 30) ? 1 : 2));
2101 if (blockSize >= kBlockSizeMax)
2102 blockSize = kBlockSizeMax;
2103
2104 UInt64 son = (UInt64)dict;
2105 if (btMode)
2106 son *= 2;
2107 const UInt64 v = (hs + son) * 4 + blockSize +
2108 (1 << 20) + (multiThread ? (6 << 20) : 0);
2109
2110 // printf("\nGetLZMAUsage = %d\n", (UInt32)(v >> 20));
2111 // printf("\nblockSize = %d\n", (UInt32)(blockSize >> 20));
2112 return v;
2113}
2114
2115
2116UInt64 GetBenchMemoryUsage(UInt32 numThreads, int level, UInt64 dictionary, bool totalBench)
2117{
2118 const size_t kBufferSize = (size_t)dictionary + kAdditionalSize;
2119 const UInt64 kCompressedBufferSize = GetBenchCompressedSize(kBufferSize); // / 2;
2120 if (level < 0)
2121 level = 5;
2122 const int algo = (level < 5 ? 0 : 1);
2123 const int btMode = (algo == 0 ? 0 : 1);
2124
2125 UInt32 numBigThreads = numThreads;
2126 bool lzmaMt = (totalBench || (numThreads > 1 && btMode));
2127 if (btMode)
2128 {
2129 if (!totalBench && lzmaMt)
2130 numBigThreads /= 2;
2131 }
2132 return ((UInt64)kBufferSize + kCompressedBufferSize +
2133 GetLZMAUsage(lzmaMt, btMode, dictionary) + (2 << 20)) * numBigThreads;
2134}
2135
2136static UInt64 GetBenchMemoryUsage_Hash(UInt32 numThreads, UInt64 dictionary)
2137{
2138 // dictionary += (dictionary >> 9); // for page tables (virtual memory)
2139 return (UInt64)(dictionary + (1 << 15)) * numThreads + (2 << 20);
2140}
2141
2142
2143// ---------- CRC and HASH ----------
2144
2145struct CCrcInfo_Base
2146{
2147 CMidAlignedBuffer Buffer;
2148 const Byte *Data;
2149 size_t Size;
2150 bool CreateLocalBuf;
2151 UInt32 CheckSum_Res;
2152
2153 CCrcInfo_Base(): CreateLocalBuf(true), CheckSum_Res(0) {}
2154
2155 HRESULT Generate(const Byte *data, size_t size);
2156 HRESULT CrcProcess(UInt64 numIterations,
2157 const UInt32 *checkSum, IHasher *hf,
2158 IBenchPrintCallback *callback);
2159};
2160
2161
2162HRESULT CCrcInfo_Base::Generate(const Byte *data, size_t size)
2163{
2164 Size = size;
2165 Data = data;
2166 if (!data || CreateLocalBuf)
2167 {
2168 ALLOC_WITH_HRESULT(&Buffer, size)
2169 Data = Buffer;
2170 }
2171 if (!data)
2172 RandGen(Buffer, size);
2173 else if (CreateLocalBuf && size != 0)
2174 memcpy(Buffer, data, size);
2175 return S_OK;
2176}
2177
2178
2179HRESULT CCrcInfo_Base::CrcProcess(UInt64 numIterations,
2180 const UInt32 *checkSum, IHasher *hf,
2181 IBenchPrintCallback *callback)
2182{
2183 MY_ALIGN(16)
2184 Byte hash[64];
2185 memset(hash, 0, sizeof(hash));
2186
2187 CheckSum_Res = 0;
2188
2189 const UInt32 hashSize = hf->GetDigestSize();
2190 if (hashSize > sizeof(hash))
2191 return S_FALSE;
2192
2193 const Byte *buf = Data;
2194 const size_t size = Size;
2195 UInt32 checkSum_Prev = 0;
2196
2197 UInt64 prev = 0;
2198 UInt64 cur = 0;
2199
2200 for (UInt64 i = 0; i < numIterations; i++)
2201 {
2202 hf->Init();
2203 size_t pos = 0;
2204 do
2205 {
2206 const size_t rem = size - pos;
2207 const UInt32 kStep = ((UInt32)1 << 31);
2208 const UInt32 curSize = (rem < kStep) ? (UInt32)rem : kStep;
2209 hf->Update(buf + pos, curSize);
2210 pos += curSize;
2211 }
2212 while (pos != size);
2213
2214 hf->Final(hash);
2215 UInt32 sum = 0;
2216 for (UInt32 j = 0; j < hashSize; j += 4)
2217 {
2218 sum = rotlFixed(sum, 11);
2219 sum += GetUi32(hash + j);
2220 }
2221 if (checkSum)
2222 {
2223 if (sum != *checkSum)
2224 return S_FALSE;
2225 }
2226 else
2227 {
2228 checkSum_Prev = sum;
2229 checkSum = &checkSum_Prev;
2230 }
2231 if (callback)
2232 {
2233 cur += size;
2234 if (cur - prev >= ((UInt32)1 << 30))
2235 {
2236 prev = cur;
2237 RINOK(callback->CheckBreak());
2238 }
2239 }
2240 }
2241 CheckSum_Res = checkSum_Prev;
2242 return S_OK;
2243}
2244
2245extern
2246UInt32 g_BenchCpuFreqTemp; // we need non-static variavble to disable compiler optimization
2247UInt32 g_BenchCpuFreqTemp = 1;
2248
2249#define YY1 sum += val; sum ^= val;
2250#define YY3 YY1 YY1 YY1 YY1
2251#define YY5 YY3 YY3 YY3 YY3
2252#define YY7 YY5 YY5 YY5 YY5
2253static const UInt32 kNumFreqCommands = 128;
2254
2255EXTERN_C_BEGIN
2256
2257static UInt32 CountCpuFreq(UInt32 sum, UInt32 num, UInt32 val)
2258{
2259 for (UInt32 i = 0; i < num; i++)
2260 {
2261 YY7
2262 }
2263 return sum;
2264}
2265
2266EXTERN_C_END
2267
2268
2269#ifndef _7ZIP_ST
2270
2271struct CBaseThreadInfo
2272{
2273 NWindows::CThread Thread;
2274 IBenchPrintCallback *Callback;
2275 HRESULT CallbackRes;
2276
2277 WRes Wait_If_Created()
2278 {
2279 if (!Thread.IsCreated())
2280 return 0;
2281 return Thread.Wait_Close();
2282 }
2283};
2284
2285struct CFreqInfo: public CBaseThreadInfo
2286{
2287 UInt32 ValRes;
2288 UInt32 Size;
2289 UInt64 NumIterations;
2290};
2291
2292static THREAD_FUNC_DECL FreqThreadFunction(void *param)
2293{
2294 CFreqInfo *p = (CFreqInfo *)param;
2295
2296 UInt32 sum = g_BenchCpuFreqTemp;
2297 for (UInt64 k = p->NumIterations; k > 0; k--)
2298 {
2299 if (p->Callback)
2300 {
2301 p->CallbackRes = p->Callback->CheckBreak();
2302 if (p->CallbackRes != S_OK)
2303 return 0;
2304 }
2305 sum = CountCpuFreq(sum, p->Size, g_BenchCpuFreqTemp);
2306 }
2307 p->ValRes = sum;
2308 return 0;
2309}
2310
2311struct CFreqThreads
2312{
2313 CFreqInfo *Items;
2314 UInt32 NumThreads;
2315
2316 CFreqThreads(): Items(NULL), NumThreads(0) {}
2317
2318 WRes WaitAll()
2319 {
2320 WRes wres = 0;
2321 for (UInt32 i = 0; i < NumThreads; i++)
2322 {
2323 WRes wres2 = Items[i].Wait_If_Created();
2324 if (wres == 0 && wres2 != 0)
2325 wres = wres2;
2326 }
2327 NumThreads = 0;
2328 return wres;
2329 }
2330
2331 ~CFreqThreads()
2332 {
2333 WaitAll();
2334 delete []Items;
2335 }
2336};
2337
2338
2339static THREAD_FUNC_DECL CrcThreadFunction(void *param);
2340
2341struct CCrcInfo: public CBaseThreadInfo
2342{
2343 const Byte *Data;
2344 size_t Size;
2345 UInt64 NumIterations;
2346 bool CheckSumDefined;
2347 UInt32 CheckSum;
2348 CMyComPtr<IHasher> Hasher;
2349 HRESULT Res;
2350 UInt32 CheckSum_Res;
2351
2352 #ifndef _7ZIP_ST
2353 NSynchronization::CManualResetEvent ReadyEvent;
2354 UInt32 ThreadIndex;
2355 CBenchSyncCommon *Common;
2356 CAffinityMode AffinityMode;
2357 #endif
2358
2359 // we want to call CCrcInfo_Base::Buffer.Free() in main thread.
2360 // so we uses non-local CCrcInfo_Base.
2361 CCrcInfo_Base crcib;
2362
2363 HRESULT CreateThread()
2364 {
2365 WRes res = 0;
2366 if (!ReadyEvent.IsCreated())
2367 res = ReadyEvent.Create();
2368 if (res == 0)
2369 res = AffinityMode.CreateThread_WithAffinity(Thread, CrcThreadFunction, this,
2370 ThreadIndex);
2371 return HRESULT_FROM_WIN32(res);
2372 }
2373
2374 #ifdef USE_ALLOCA
2375 size_t AllocaSize;
2376 #endif
2377
2378 void Process();
2379
2380 CCrcInfo(): Res(E_FAIL) {}
2381};
2382
2383static const bool k_Crc_CreateLocalBuf_For_File = true; // for total BW test
2384// static const bool k_Crc_CreateLocalBuf_For_File = false; // for shared memory read test
2385
2386void CCrcInfo::Process()
2387{
2388 crcib.CreateLocalBuf = k_Crc_CreateLocalBuf_For_File;
2389 // we can use additional Generate() passes to reduce some time effects for new page allocation
2390 // for (unsigned y = 0; y < 10; y++)
2391 Res = crcib.Generate(Data, Size);
2392
2393 // if (Common)
2394 {
2395 WRes wres = ReadyEvent.Set();
2396 if (wres != 0)
2397 {
2398 if (Res == 0)
2399 Res = HRESULT_FROM_WIN32(wres);
2400 return;
2401 }
2402 if (Res != 0)
2403 return;
2404
2405 wres = Common->StartEvent.Lock();
2406
2407 if (wres != 0)
2408 {
2409 Res = HRESULT_FROM_WIN32(wres);
2410 return;
2411 }
2412 if (Common->ExitMode)
2413 return;
2414 }
2415
2416 Res = crcib.CrcProcess(NumIterations,
2417 CheckSumDefined ? &CheckSum : NULL, Hasher,
2418 Callback);
2419 CheckSum_Res = crcib.CheckSum_Res;
2420 /*
2421 We don't want to include the time of slow CCrcInfo_Base::Buffer.Free()
2422 to time of benchmark. So we don't free Buffer here
2423 */
2424 // crcib.Buffer.Free();
2425}
2426
2427
2428static THREAD_FUNC_DECL CrcThreadFunction(void *param)
2429{
2430 CCrcInfo *p = (CCrcInfo *)param;
2431
2432 #ifdef USE_ALLOCA
2433 alloca(p->AllocaSize);
2434 #endif
2435 p->Process();
2436 return 0;
2437}
2438
2439
2440struct CCrcThreads
2441{
2442 CCrcInfo *Items;
2443 unsigned NumThreads;
2444 CBenchSyncCommon Common;
2445 bool NeedClose;
2446
2447 CCrcThreads(): Items(NULL), NumThreads(0), NeedClose(false) {}
2448
2449 WRes StartAndWait(bool exitMode = false);
2450
2451 ~CCrcThreads()
2452 {
2453 StartAndWait(true);
2454 delete []Items;
2455 }
2456};
2457
2458
2459WRes CCrcThreads::StartAndWait(bool exitMode)
2460{
2461 if (!NeedClose)
2462 return 0;
2463
2464 Common.ExitMode = exitMode;
2465 WRes wres = Common.StartEvent.Set();
2466
2467 for (unsigned i = 0; i < NumThreads; i++)
2468 {
2469 WRes wres2 = Items[i].Wait_If_Created();
2470 if (wres == 0 && wres2 != 0)
2471 wres = wres2;
2472 }
2473 NumThreads = 0;
2474 NeedClose = false;
2475 return wres;
2476}
2477
2478#endif
2479
2480
2481static UInt32 CrcCalc1(const Byte *buf, size_t size)
2482{
2483 UInt32 crc = CRC_INIT_VAL;;
2484 for (size_t i = 0; i < size; i++)
2485 crc = CRC_UPDATE_BYTE(crc, buf[i]);
2486 return CRC_GET_DIGEST(crc);
2487}
2488
2489/*
2490static UInt32 RandGenCrc(Byte *buf, size_t size, CBaseRandomGenerator &RG)
2491{
2492 RandGen(buf, size, RG);
2493 return CrcCalc1(buf, size);
2494}
2495*/
2496
2497static bool CrcInternalTest()
2498{
2499 CAlignedBuffer buffer;
2500 const size_t kBufferSize0 = (1 << 8);
2501 const size_t kBufferSize1 = (1 << 10);
2502 const unsigned kCheckSize = (1 << 5);
2503 buffer.Alloc(kBufferSize0 + kBufferSize1);
2504 if (!buffer.IsAllocated())
2505 return false;
2506 Byte *buf = (Byte *)buffer;
2507 size_t i;
2508 for (i = 0; i < kBufferSize0; i++)
2509 buf[i] = (Byte)i;
2510 UInt32 crc1 = CrcCalc1(buf, kBufferSize0);
2511 if (crc1 != 0x29058C73)
2512 return false;
2513 RandGen(buf + kBufferSize0, kBufferSize1);
2514 for (i = 0; i < kBufferSize0 + kBufferSize1 - kCheckSize; i++)
2515 for (unsigned j = 0; j < kCheckSize; j++)
2516 if (CrcCalc1(buf + i, j) != CrcCalc(buf + i, j))
2517 return false;
2518 return true;
2519}
2520
2521struct CBenchMethod
2522{
2523 unsigned Weight;
2524 unsigned DictBits;
2525 UInt32 EncComplex;
2526 UInt32 DecComplexCompr;
2527 UInt32 DecComplexUnc;
2528 const char *Name;
2529 // unsigned KeySize;
2530};
2531
2532// #define USE_SW_CMPLX
2533
2534#ifdef USE_SW_CMPLX
2535#define CMPLX(x) ((x) * 1000)
2536#else
2537#define CMPLX(x) (x)
2538#endif
2539
2540static const CBenchMethod g_Bench[] =
2541{
2542 // { 40, 17, 357, 145, 20, "LZMA:x1" },
2543 // { 20, 18, 360, 145, 20, "LZMA2:x1:mt2" },
2544
2545 { 20, 18, 360, 145, 20, "LZMA:x1" },
2546 { 20, 22, 600, 145, 20, "LZMA:x3" },
2547
2548 { 80, 24, 1220, 145, 20, "LZMA:x5:mt1" },
2549 { 80, 24, 1220, 145, 20, "LZMA:x5:mt2" },
2550
2551 { 10, 16, 124, 40, 14, "Deflate:x1" },
2552 { 20, 16, 376, 40, 14, "Deflate:x5" },
2553 { 10, 16, 1082, 40, 14, "Deflate:x7" },
2554 { 10, 17, 422, 40, 14, "Deflate64:x5" },
2555
2556 { 10, 15, 590, 69, 69, "BZip2:x1" },
2557 { 20, 19, 815, 122, 122, "BZip2:x5" },
2558 { 10, 19, 815, 122, 122, "BZip2:x5:mt2" },
2559 { 10, 19, 2530, 122, 122, "BZip2:x7" },
2560
2561 // { 10, 18, 1010, 0, 1150, "PPMDZip:x1" },
2562 { 10, 18, 1010, 0, 1150, "PPMD:x1" },
2563 // { 10, 22, 1655, 0, 1830, "PPMDZip:x5" },
2564 { 10, 22, 1655, 0, 1830, "PPMD:x5" },
2565
2566 // { 2, 0, 3, 0, 4, "Delta:1" },
2567 // { 2, 0, 3, 0, 4, "Delta:2" },
2568 // { 2, 0, 3, 0, 4, "Delta:3" },
2569 { 2, 0, 3, 0, 4, "Delta:4" },
2570 // { 2, 0, 3, 0, 4, "Delta:8" },
2571 // { 2, 0, 3, 0, 4, "Delta:32" },
2572
2573 { 2, 0, 4, 0, 4, "BCJ" },
2574
2575 // { 10, 0, 18, 0, 18, "AES128CBC:1" },
2576 // { 10, 0, 21, 0, 21, "AES192CBC:1" },
2577 { 10, 0, 24, 0, 24, "AES256CBC:1" },
2578
2579 // { 10, 0, 18, 0, 18, "AES128CTR:1" },
2580 // { 10, 0, 21, 0, 21, "AES192CTR:1" },
2581 // { 10, 0, 24, 0, 24, "AES256CTR:1" },
2582 // { 2, 0, CMPLX(6), 0, CMPLX(1), "AES128CBC:2" },
2583 // { 2, 0, CMPLX(7), 0, CMPLX(1), "AES192CBC:2" },
2584 { 2, 0, CMPLX(8), 0, CMPLX(1), "AES256CBC:2" },
2585
2586 // { 2, 0, CMPLX(1), 0, CMPLX(1), "AES128CTR:2" },
2587 // { 2, 0, CMPLX(1), 0, CMPLX(1), "AES192CTR:2" },
2588 // { 2, 0, CMPLX(1), 0, CMPLX(1), "AES256CTR:2" },
2589
2590 // { 1, 0, CMPLX(6), 0, CMPLX(1), "AES128CBC:3" },
2591 // { 1, 0, CMPLX(7), 0, CMPLX(1), "AES192CBC:3" },
2592 { 1, 0, CMPLX(8), 0, CMPLX(1), "AES256CBC:3" }
2593
2594 // { 1, 0, CMPLX(1), 0, CMPLX(1), "AES128CTR:3" },
2595 // { 1, 0, CMPLX(1), 0, CMPLX(1), "AES192CTR:3" },
2596 // { 1, 0, CMPLX(1), 0, CMPLX(1), "AES256CTR:3" },
2597};
2598
2599struct CBenchHash
2600{
2601 unsigned Weight;
2602 UInt32 Complex;
2603 UInt32 CheckSum;
2604 const char *Name;
2605};
2606
2607// #define ARM_CRC_MUL 100
2608#define ARM_CRC_MUL 1
2609
2610static const CBenchHash g_Hash[] =
2611{
2612 { 1, 1820, 0x21e207bb, "CRC32:1" },
2613 { 10, 558, 0x21e207bb, "CRC32:4" },
2614 { 10, 339, 0x21e207bb, "CRC32:8" } ,
2615 { 2, 128 *ARM_CRC_MUL, 0x21e207bb, "CRC32:32" },
2616 { 2, 64 *ARM_CRC_MUL, 0x21e207bb, "CRC32:64" },
2617 { 10, 512, 0x41b901d1, "CRC64" },
2618
2619 { 10, 5100, 0x7913ba03, "SHA256:1" },
2620 { 2, CMPLX((32 * 4 + 1) * 4 + 4), 0x7913ba03, "SHA256:2" },
2621
2622 { 10, 2340, 0xff769021, "SHA1:1" },
2623 { 2, CMPLX((20 * 6 + 1) * 4 + 4), 0xff769021, "SHA1:2" },
2624
2625 { 2, 5500, 0x85189d02, "BLAKE2sp" }
2626};
2627
2628static void PrintNumber(IBenchPrintCallback &f, UInt64 value, unsigned size)
2629{
2630 char s[128];
2631 unsigned startPos = (unsigned)sizeof(s) - 32;
2632 memset(s, ' ', startPos);
2633 ConvertUInt64ToString(value, s + startPos);
2634 // if (withSpace)
2635 {
2636 startPos--;
2637 size++;
2638 }
2639 unsigned len = (unsigned)strlen(s + startPos);
2640 if (size > len)
2641 {
2642 size -= len;
2643 if (startPos < size)
2644 startPos = 0;
2645 else
2646 startPos -= size;
2647 }
2648 f.Print(s + startPos);
2649}
2650
2651static const unsigned kFieldSize_Name = 12;
2652static const unsigned kFieldSize_SmallName = 4;
2653static const unsigned kFieldSize_Speed = 9;
2654static const unsigned kFieldSize_Usage = 5;
2655static const unsigned kFieldSize_RU = 6;
2656static const unsigned kFieldSize_Rating = 6;
2657static const unsigned kFieldSize_EU = 5;
2658static const unsigned kFieldSize_Effec = 5;
2659
2660static const unsigned kFieldSize_TotalSize = 4 + kFieldSize_Speed + kFieldSize_Usage + kFieldSize_RU + kFieldSize_Rating;
2661static const unsigned kFieldSize_EUAndEffec = 2 + kFieldSize_EU + kFieldSize_Effec;
2662
2663
2664static void PrintRating(IBenchPrintCallback &f, UInt64 rating, unsigned size)
2665{
2666 PrintNumber(f, (rating + 500000) / 1000000, size);
2667}
2668
2669
2670static void PrintPercents(IBenchPrintCallback &f, UInt64 val, UInt64 divider, unsigned size)
2671{
2672 UInt64 v = 0;
2673 if (divider != 0)
2674 v = (val * 100 + divider / 2) / divider;
2675 PrintNumber(f, v, size);
2676}
2677
2678static void PrintChars(IBenchPrintCallback &f, char c, unsigned size)
2679{
2680 char s[256];
2681 memset(s, (Byte)c, size);
2682 s[size] = 0;
2683 f.Print(s);
2684}
2685
2686static void PrintSpaces(IBenchPrintCallback &f, unsigned size)
2687{
2688 PrintChars(f, ' ', size);
2689}
2690
2691static void PrintUsage(IBenchPrintCallback &f, UInt64 usage, unsigned size)
2692{
2693 PrintNumber(f, Benchmark_GetUsage_Percents(usage), size);
2694}
2695
2696static void PrintResults(IBenchPrintCallback &f, UInt64 usage, UInt64 rpu, UInt64 rating, bool showFreq, UInt64 cpuFreq)
2697{
2698 PrintUsage(f, usage, kFieldSize_Usage);
2699 PrintRating(f, rpu, kFieldSize_RU);
2700 PrintRating(f, rating, kFieldSize_Rating);
2701 if (showFreq)
2702 {
2703 if (cpuFreq == 0)
2704 PrintSpaces(f, kFieldSize_EUAndEffec);
2705 else
2706 {
2707 PrintPercents(f, rating, cpuFreq * usage / kBenchmarkUsageMult, kFieldSize_EU);
2708 PrintPercents(f, rating, cpuFreq, kFieldSize_Effec);
2709 }
2710 }
2711}
2712
2713
2714void CTotalBenchRes::Generate_From_BenchInfo(const CBenchInfo &info)
2715{
2716 Speed = info.GetUnpackSizeSpeed();
2717 Usage = info.GetUsage();
2718 RPU = info.GetRatingPerUsage(Rating);
2719}
2720
2721void CTotalBenchRes::Mult_For_Weight(unsigned weight)
2722{
2723 NumIterations2 *= weight;
2724 RPU *= weight;
2725 Rating *= weight;
2726 Usage *= weight;
2727 Speed *= weight;
2728}
2729
2730void CTotalBenchRes::Update_With_Res(const CTotalBenchRes &r)
2731{
2732 Rating += r.Rating;
2733 Usage += r.Usage;
2734 RPU += r.RPU;
2735 Speed += r.Speed;
2736 // NumIterations1 = (r1.NumIterations1 + r2.NumIterations1);
2737 NumIterations2 += r.NumIterations2;
2738}
2739
2740static void PrintResults(IBenchPrintCallback *f,
2741 const CBenchInfo &info,
2742 unsigned weight,
2743 UInt64 rating,
2744 bool showFreq, UInt64 cpuFreq,
2745 CTotalBenchRes *res)
2746{
2747 CTotalBenchRes t;
2748 t.Rating = rating;
2749 t.NumIterations2 = 1;
2750 t.Generate_From_BenchInfo(info);
2751
2752 if (f)
2753 {
2754 if (t.Speed != 0)
2755 PrintNumber(*f, t.Speed / 1024, kFieldSize_Speed);
2756 else
2757 PrintSpaces(*f, 1 + kFieldSize_Speed);
2758 }
2759 if (f)
2760 {
2761 PrintResults(*f, t.Usage, t.RPU, rating, showFreq, cpuFreq);
2762 }
2763
2764 if (res)
2765 {
2766 // res->NumIterations1++;
2767 t.Mult_For_Weight(weight);
2768 res->Update_With_Res(t);
2769 }
2770}
2771
2772static void PrintTotals(IBenchPrintCallback &f,
2773 bool showFreq, UInt64 cpuFreq, bool showSpeed, const CTotalBenchRes &res)
2774{
2775 const UInt64 numIterations2 = res.NumIterations2 ? res.NumIterations2 : 1;
2776 const UInt64 speed = res.Speed / numIterations2;
2777 if (showSpeed && speed != 0)
2778 PrintNumber(f, speed / 1024, kFieldSize_Speed);
2779 else
2780 PrintSpaces(f, 1 + kFieldSize_Speed);
2781
2782 // PrintSpaces(f, 1 + kFieldSize_Speed);
2783 // UInt64 numIterations1 = res.NumIterations1; if (numIterations1 == 0) numIterations1 = 1;
2784 PrintResults(f, res.Usage / numIterations2, res.RPU / numIterations2, res.Rating / numIterations2, showFreq, cpuFreq);
2785}
2786
2787
2788static void PrintHex(AString &s, UInt64 v)
2789{
2790 char temp[32];
2791 ConvertUInt64ToHex(v, temp);
2792 s += temp;
2793}
2794
2795AString GetProcessThreadsInfo(const NSystem::CProcessAffinity &ti)
2796{
2797 AString s;
2798 // s.Add_UInt32(ti.numProcessThreads);
2799 unsigned numSysThreads = ti.GetNumSystemThreads();
2800 if (ti.GetNumProcessThreads() != numSysThreads)
2801 {
2802 // if (ti.numProcessThreads != ti.numSysThreads)
2803 {
2804 s += " / ";
2805 s.Add_UInt32(numSysThreads);
2806 }
2807 s += " : ";
2808 #ifdef _WIN32
2809 PrintHex(s, ti.processAffinityMask);
2810 s += " / ";
2811 PrintHex(s, ti.systemAffinityMask);
2812 #else
2813 unsigned i = (numSysThreads + 3) & ~(unsigned)3;
2814 if (i == 0)
2815 i = 4;
2816 for (; i >= 4; )
2817 {
2818 i -= 4;
2819 unsigned val = 0;
2820 for (unsigned k = 0; k < 4; k++)
2821 {
2822 const unsigned bit = (ti.IsCpuSet(i + k) ? 1 : 0);
2823 val += (bit << k);
2824 }
2825 PrintHex(s, val);
2826 }
2827 #endif
2828 }
2829 return s;
2830}
2831
2832
2833#ifdef _7ZIP_LARGE_PAGES
2834
2835#ifdef _WIN32
2836extern bool g_LargePagesMode;
2837extern "C"
2838{
2839 extern SIZE_T g_LargePageSize;
2840}
2841#endif
2842
2843void Add_LargePages_String(AString &s)
2844{
2845 #ifdef _WIN32
2846 if (g_LargePagesMode || g_LargePageSize != 0)
2847 {
2848 s.Add_OptSpaced("(LP-");
2849 PrintSize_KMGT_Or_Hex(s, g_LargePageSize);
2850 #ifdef MY_CPU_X86_OR_AMD64
2851 if (CPU_IsSupported_PageGB())
2852 s += "-1G";
2853 #endif
2854 if (!g_LargePagesMode)
2855 s += "-NA";
2856 s += ")";
2857 }
2858 #else
2859 s += "";
2860 #endif
2861}
2862
2863#endif
2864
2865
2866
2867static void PrintRequirements(IBenchPrintCallback &f, const char *sizeString,
2868 bool size_Defined, UInt64 size, const char *threadsString, UInt32 numThreads)
2869{
2870 f.Print("RAM ");
2871 f.Print(sizeString);
2872 if (size_Defined)
2873 PrintNumber(f, (size >> 20), 6);
2874 else
2875 f.Print(" ?");
2876 f.Print(" MB");
2877
2878 #ifdef _7ZIP_LARGE_PAGES
2879 {
2880 AString s;
2881 Add_LargePages_String(s);
2882 f.Print(s);
2883 }
2884 #endif
2885
2886 f.Print(", # ");
2887 f.Print(threadsString);
2888 PrintNumber(f, numThreads, 3);
2889}
2890
2891
2892
2893struct CBenchCallbackToPrint: public IBenchCallback
2894{
2895 CBenchProps BenchProps;
2896 CTotalBenchRes EncodeRes;
2897 CTotalBenchRes DecodeRes;
2898 IBenchPrintCallback *_file;
2899 UInt64 DictSize;
2900
2901 bool Use2Columns;
2902 unsigned NameFieldSize;
2903
2904 bool ShowFreq;
2905 UInt64 CpuFreq;
2906
2907 unsigned EncodeWeight;
2908 unsigned DecodeWeight;
2909
2910 CBenchCallbackToPrint():
2911 Use2Columns(false),
2912 NameFieldSize(0),
2913 ShowFreq(false),
2914 CpuFreq(0),
2915 EncodeWeight(1),
2916 DecodeWeight(1)
2917 {}
2918
2919 void Init() { EncodeRes.Init(); DecodeRes.Init(); }
2920 void Print(const char *s);
2921 void NewLine();
2922
2923 HRESULT SetFreq(bool showFreq, UInt64 cpuFreq);
2924 HRESULT SetEncodeResult(const CBenchInfo &info, bool final);
2925 HRESULT SetDecodeResult(const CBenchInfo &info, bool final);
2926};
2927
2928HRESULT CBenchCallbackToPrint::SetFreq(bool showFreq, UInt64 cpuFreq)
2929{
2930 ShowFreq = showFreq;
2931 CpuFreq = cpuFreq;
2932 return S_OK;
2933}
2934
2935HRESULT CBenchCallbackToPrint::SetEncodeResult(const CBenchInfo &info, bool final)
2936{
2937 RINOK(_file->CheckBreak());
2938 if (final)
2939 {
2940 UInt64 rating = BenchProps.GetCompressRating(DictSize, info.GlobalTime, info.GlobalFreq, info.UnpackSize * info.NumIterations);
2941 PrintResults(_file, info,
2942 EncodeWeight, rating,
2943 ShowFreq, CpuFreq, &EncodeRes);
2944 if (!Use2Columns)
2945 _file->NewLine();
2946 }
2947 return S_OK;
2948}
2949
2950static const char * const kSep = " | ";
2951
2952HRESULT CBenchCallbackToPrint::SetDecodeResult(const CBenchInfo &info, bool final)
2953{
2954 RINOK(_file->CheckBreak());
2955 if (final)
2956 {
2957 UInt64 rating = BenchProps.GetDecompressRating(info.GlobalTime, info.GlobalFreq, info.UnpackSize, info.PackSize, info.NumIterations);
2958 if (Use2Columns)
2959 _file->Print(kSep);
2960 else
2961 PrintSpaces(*_file, NameFieldSize);
2962 CBenchInfo info2 = info;
2963 info2.UnpackSize *= info2.NumIterations;
2964 info2.PackSize *= info2.NumIterations;
2965 info2.NumIterations = 1;
2966 PrintResults(_file, info2,
2967 DecodeWeight, rating,
2968 ShowFreq, CpuFreq, &DecodeRes);
2969 }
2970 return S_OK;
2971}
2972
2973void CBenchCallbackToPrint::Print(const char *s)
2974{
2975 _file->Print(s);
2976}
2977
2978void CBenchCallbackToPrint::NewLine()
2979{
2980 _file->NewLine();
2981}
2982
2983static void PrintLeft(IBenchPrintCallback &f, const char *s, unsigned size)
2984{
2985 f.Print(s);
2986 int numSpaces = (int)size - (int)MyStringLen(s);
2987 if (numSpaces > 0)
2988 PrintSpaces(f, (unsigned)numSpaces);
2989}
2990
2991static void PrintRight(IBenchPrintCallback &f, const char *s, unsigned size)
2992{
2993 int numSpaces = (int)size - (int)MyStringLen(s);
2994 if (numSpaces > 0)
2995 PrintSpaces(f, (unsigned)numSpaces);
2996 f.Print(s);
2997}
2998
2999static HRESULT TotalBench(
3000 DECL_EXTERNAL_CODECS_LOC_VARS
3001 UInt64 complexInCommands,
3002 #ifndef _7ZIP_ST
3003 UInt32 numThreads,
3004 const CAffinityMode *affinityMode,
3005 #endif
3006 bool forceUnpackSize,
3007 size_t unpackSize,
3008 const Byte *fileData,
3009 IBenchPrintCallback *printCallback, CBenchCallbackToPrint *callback)
3010{
3011 for (unsigned i = 0; i < ARRAY_SIZE(g_Bench); i++)
3012 {
3013 const CBenchMethod &bench = g_Bench[i];
3014 PrintLeft(*callback->_file, bench.Name, kFieldSize_Name);
3015 {
3016 unsigned keySize = 32;
3017 if (IsString1PrefixedByString2(bench.Name, "AES128")) keySize = 16;
3018 else if (IsString1PrefixedByString2(bench.Name, "AES192")) keySize = 24;
3019 callback->BenchProps.KeySize = keySize;
3020 }
3021 callback->BenchProps.DecComplexUnc = bench.DecComplexUnc;
3022 callback->BenchProps.DecComplexCompr = bench.DecComplexCompr;
3023 callback->BenchProps.EncComplex = bench.EncComplex;
3024
3025 COneMethodInfo method;
3026 NCOM::CPropVariant propVariant;
3027 propVariant = bench.Name;
3028 RINOK(method.ParseMethodFromPROPVARIANT(UString(), propVariant));
3029
3030 size_t unpackSize2 = unpackSize;
3031 if (!forceUnpackSize && bench.DictBits == 0)
3032 unpackSize2 = kFilterUnpackSize;
3033
3034 callback->EncodeWeight = bench.Weight;
3035 callback->DecodeWeight = bench.Weight;
3036
3037 HRESULT res = MethodBench(
3038 EXTERNAL_CODECS_LOC_VARS
3039 complexInCommands,
3040 #ifndef _7ZIP_ST
3041 false, numThreads, affinityMode,
3042 #endif
3043 method,
3044 unpackSize2, fileData,
3045 bench.DictBits,
3046 printCallback, callback, &callback->BenchProps);
3047
3048 if (res == E_NOTIMPL)
3049 {
3050 // callback->Print(" ---");
3051 // we need additional empty line as line for decompression results
3052 if (!callback->Use2Columns)
3053 callback->NewLine();
3054 }
3055 else
3056 {
3057 RINOK(res);
3058 }
3059
3060 callback->NewLine();
3061 }
3062 return S_OK;
3063}
3064
3065
3066struct CFreqBench
3067{
3068 // in:
3069 UInt64 complexInCommands;
3070 UInt32 numThreads;
3071 bool showFreq;
3072 UInt64 specifiedFreq;
3073
3074 // out:
3075 UInt64 CpuFreqRes;
3076 UInt64 UsageRes;
3077 UInt32 res;
3078
3079 CFreqBench()
3080 {}
3081
3082 HRESULT FreqBench(IBenchPrintCallback *_file
3083 #ifndef _7ZIP_ST
3084 , const CAffinityMode *affinityMode
3085 #endif
3086 );
3087};
3088
3089
3090HRESULT CFreqBench::FreqBench(IBenchPrintCallback *_file
3091 #ifndef _7ZIP_ST
3092 , const CAffinityMode *affinityMode
3093 #endif
3094 )
3095{
3096 res = 0;
3097 CpuFreqRes = 0;
3098 UsageRes = 0;
3099
3100 if (numThreads == 0)
3101 numThreads = 1;
3102
3103 #ifdef _7ZIP_ST
3104 numThreads = 1;
3105 #endif
3106
3107 const UInt32 complexity = kNumFreqCommands;
3108 UInt64 numIterations = complexInCommands / complexity;
3109 UInt32 numIterations2 = 1 << 30;
3110 if (numIterations > numIterations2)
3111 numIterations /= numIterations2;
3112 else
3113 {
3114 numIterations2 = (UInt32)numIterations;
3115 numIterations = 1;
3116 }
3117
3118 CBenchInfoCalc progressInfoSpec;
3119
3120 #ifndef _7ZIP_ST
3121
3122 bool mtMode = (numThreads > 1) || affinityMode->NeedAffinity();
3123
3124 if (mtMode)
3125 {
3126 CFreqThreads threads;
3127 threads.Items = new CFreqInfo[numThreads];
3128 UInt32 i;
3129 for (i = 0; i < numThreads; i++)
3130 {
3131 CFreqInfo &info = threads.Items[i];
3132 info.Callback = _file;
3133 info.CallbackRes = S_OK;
3134 info.NumIterations = numIterations;
3135 info.Size = numIterations2;
3136 }
3137 progressInfoSpec.SetStartTime();
3138 for (i = 0; i < numThreads; i++)
3139 {
3140 // Sleep(10);
3141 CFreqInfo &info = threads.Items[i];
3142 WRes wres = affinityMode->CreateThread_WithAffinity(info.Thread, FreqThreadFunction, &info, i);
3143 if (info.Thread.IsCreated())
3144 threads.NumThreads++;
3145 if (wres != 0)
3146 return HRESULT_FROM_WIN32(wres);
3147 }
3148 WRes wres = threads.WaitAll();
3149 if (wres != 0)
3150 return HRESULT_FROM_WIN32(wres);
3151 for (i = 0; i < numThreads; i++)
3152 {
3153 RINOK(threads.Items[i].CallbackRes);
3154 }
3155 }
3156 else
3157 #endif
3158 {
3159 progressInfoSpec.SetStartTime();
3160 UInt32 sum = g_BenchCpuFreqTemp;
3161 for (UInt64 k = numIterations; k > 0; k--)
3162 {
3163 sum = CountCpuFreq(sum, numIterations2, g_BenchCpuFreqTemp);
3164 if (_file)
3165 {
3166 RINOK(_file->CheckBreak());
3167 }
3168 }
3169 res += sum;
3170 }
3171
3172 if (res == 0x12345678)
3173 if (_file)
3174 {
3175 RINOK(_file->CheckBreak());
3176 }
3177
3178 CBenchInfo info;
3179 progressInfoSpec.SetFinishTime(info);
3180
3181 info.UnpackSize = 0;
3182 info.PackSize = 0;
3183 info.NumIterations = 1;
3184
3185 const UInt64 numCommands = (UInt64)numIterations * numIterations2 * numThreads * complexity;
3186 const UInt64 rating = info.GetSpeed(numCommands);
3187 CpuFreqRes = rating / numThreads;
3188 UsageRes = info.GetUsage();
3189
3190 if (_file)
3191 {
3192 PrintResults(_file, info,
3193 0, // weight
3194 rating,
3195 showFreq, showFreq ? (specifiedFreq != 0 ? specifiedFreq : CpuFreqRes) : 0, NULL);
3196 RINOK(_file->CheckBreak());
3197 }
3198
3199 return S_OK;
3200}
3201
3202
3203
3204static HRESULT CrcBench(
3205 DECL_EXTERNAL_CODECS_LOC_VARS
3206 UInt64 complexInCommands,
3207 UInt32 numThreads,
3208 const size_t bufferSize,
3209 const Byte *fileData,
3210
3211 UInt64 &speed,
3212 UInt64 &usage,
3213
3214 UInt32 complexity, unsigned benchWeight,
3215 const UInt32 *checkSum,
3216 const COneMethodInfo &method,
3217 IBenchPrintCallback *_file,
3218 #ifndef _7ZIP_ST
3219 const CAffinityMode *affinityMode,
3220 #endif
3221 bool showRating,
3222 CTotalBenchRes *encodeRes,
3223 bool showFreq, UInt64 cpuFreq)
3224{
3225 if (numThreads == 0)
3226 numThreads = 1;
3227
3228 #ifdef _7ZIP_ST
3229 numThreads = 1;
3230 #endif
3231
3232 const AString &methodName = method.MethodName;
3233 // methodName.RemoveChar(L'-');
3234 CMethodId hashID;
3235 if (!FindHashMethod(
3236 EXTERNAL_CODECS_LOC_VARS
3237 methodName, hashID))
3238 return E_NOTIMPL;
3239
3240 /*
3241 // if will generate random data in each thread, instead of global data
3242 CMidAlignedBuffer buffer;
3243 if (!fileData)
3244 {
3245 ALLOC_WITH_HRESULT(&buffer, bufferSize)
3246 RandGen(buffer, bufferSize);
3247 fileData = buffer;
3248 }
3249 */
3250
3251 const size_t bsize = (bufferSize == 0 ? 1 : bufferSize);
3252 UInt64 numIterations = complexInCommands * 256 / complexity / bsize;
3253 if (numIterations == 0)
3254 numIterations = 1;
3255
3256 CBenchInfoCalc progressInfoSpec;
3257 CBenchInfo info;
3258
3259 #ifndef _7ZIP_ST
3260 bool mtEncMode = (numThreads > 1) || affinityMode->NeedAffinity();
3261
3262 if (mtEncMode)
3263 {
3264 CCrcThreads threads;
3265 threads.Items = new CCrcInfo[numThreads];
3266 {
3267 WRes wres = threads.Common.StartEvent.Create();
3268 if (wres != 0)
3269 return HRESULT_FROM_WIN32(wres);
3270 threads.NeedClose = true;
3271 }
3272
3273 UInt32 i;
3274 for (i = 0; i < numThreads; i++)
3275 {
3276 CCrcInfo &ci = threads.Items[i];
3277 AString name;
3278 RINOK(CreateHasher(EXTERNAL_CODECS_LOC_VARS hashID, name, ci.Hasher));
3279 if (!ci.Hasher)
3280 return E_NOTIMPL;
3281 CMyComPtr<ICompressSetCoderProperties> scp;
3282 ci.Hasher.QueryInterface(IID_ICompressSetCoderProperties, &scp);
3283 if (scp)
3284 {
3285 RINOK(method.SetCoderProps(scp));
3286 }
3287
3288 ci.Callback = _file;
3289 ci.Data = fileData;
3290 ci.NumIterations = numIterations;
3291 ci.Size = bufferSize;
3292 ci.CheckSumDefined = false;
3293 if (checkSum)
3294 {
3295 ci.CheckSum = *checkSum;
3296 ci.CheckSumDefined = true;
3297 }
3298
3299 #ifdef USE_ALLOCA
3300 ci.AllocaSize = (i * 16 * 21) & 0x7FF;
3301 #endif
3302 }
3303
3304 for (i = 0; i < numThreads; i++)
3305 {
3306 CCrcInfo &ci = threads.Items[i];
3307 ci.ThreadIndex = i;
3308 ci.Common = &threads.Common;
3309 ci.AffinityMode = *affinityMode;
3310 HRESULT hres = ci.CreateThread();
3311 if (ci.Thread.IsCreated())
3312 threads.NumThreads++;
3313 if (hres != 0)
3314 return hres;
3315 }
3316
3317 for (i = 0; i < numThreads; i++)
3318 {
3319 CCrcInfo &ci = threads.Items[i];
3320 WRes wres = ci.ReadyEvent.Lock();
3321 if (wres != 0)
3322 return HRESULT_FROM_WIN32(wres);
3323 RINOK(ci.Res);
3324 }
3325
3326 progressInfoSpec.SetStartTime();
3327
3328 WRes wres = threads.StartAndWait();
3329 if (wres != 0)
3330 return HRESULT_FROM_WIN32(wres);
3331
3332 progressInfoSpec.SetFinishTime(info);
3333
3334 for (i = 0; i < numThreads; i++)
3335 {
3336 RINOK(threads.Items[i].Res);
3337 if (i != 0)
3338 if (threads.Items[i].CheckSum_Res !=
3339 threads.Items[i - 1].CheckSum_Res)
3340 return S_FALSE;
3341 }
3342 }
3343 else
3344 #endif
3345 {
3346 CMyComPtr<IHasher> hasher;
3347 AString name;
3348 RINOK(CreateHasher(EXTERNAL_CODECS_LOC_VARS hashID, name, hasher));
3349 if (!hasher)
3350 return E_NOTIMPL;
3351 CMyComPtr<ICompressSetCoderProperties> scp;
3352 hasher.QueryInterface(IID_ICompressSetCoderProperties, &scp);
3353 if (scp)
3354 {
3355 RINOK(method.SetCoderProps(scp));
3356 }
3357 CCrcInfo_Base crcib;
3358 crcib.CreateLocalBuf = false;
3359 RINOK(crcib.Generate(fileData, bufferSize));
3360 progressInfoSpec.SetStartTime();
3361 RINOK(crcib.CrcProcess(numIterations, checkSum, hasher, _file));
3362 progressInfoSpec.SetFinishTime(info);
3363 }
3364
3365
3366 UInt64 unpSize = numIterations * bufferSize;
3367 UInt64 unpSizeThreads = unpSize * numThreads;
3368 info.UnpackSize = unpSizeThreads;
3369 info.PackSize = unpSizeThreads;
3370 info.NumIterations = 1;
3371
3372 if (_file)
3373 {
3374 if (showRating)
3375 {
3376 UInt64 unpSizeThreads2 = unpSizeThreads;
3377 if (unpSizeThreads2 == 0)
3378 unpSizeThreads2 = numIterations * 1 * numThreads;
3379 const UInt64 numCommands = unpSizeThreads2 * complexity / 256;
3380 const UInt64 rating = info.GetSpeed(numCommands);
3381 PrintResults(_file, info,
3382 benchWeight, rating,
3383 showFreq, cpuFreq, encodeRes);
3384 }
3385 RINOK(_file->CheckBreak());
3386 }
3387
3388 speed = info.GetSpeed(unpSizeThreads);
3389 usage = info.GetUsage();
3390
3391 return S_OK;
3392}
3393
3394
3395
3396static HRESULT TotalBench_Hash(
3397 DECL_EXTERNAL_CODECS_LOC_VARS
3398 UInt64 complexInCommands,
3399 UInt32 numThreads,
3400 size_t bufSize,
3401 const Byte *fileData,
3402 IBenchPrintCallback *printCallback, CBenchCallbackToPrint *callback,
3403 #ifndef _7ZIP_ST
3404 const CAffinityMode *affinityMode,
3405 #endif
3406 CTotalBenchRes *encodeRes,
3407 bool showFreq, UInt64 cpuFreq)
3408{
3409 for (unsigned i = 0; i < ARRAY_SIZE(g_Hash); i++)
3410 {
3411 const CBenchHash &bench = g_Hash[i];
3412 PrintLeft(*callback->_file, bench.Name, kFieldSize_Name);
3413 // callback->BenchProps.DecComplexUnc = bench.DecComplexUnc;
3414 // callback->BenchProps.DecComplexCompr = bench.DecComplexCompr;
3415 // callback->BenchProps.EncComplex = bench.EncComplex;
3416
3417 COneMethodInfo method;
3418 NCOM::CPropVariant propVariant;
3419 propVariant = bench.Name;
3420 RINOK(method.ParseMethodFromPROPVARIANT(UString(), propVariant));
3421
3422 UInt64 speed, usage;
3423
3424 HRESULT res = CrcBench(
3425 EXTERNAL_CODECS_LOC_VARS
3426 complexInCommands,
3427 numThreads, bufSize, fileData,
3428 speed, usage,
3429 bench.Complex, bench.Weight,
3430 (!fileData && bufSize == (1 << kNumHashDictBits)) ? &bench.CheckSum : NULL,
3431 method,
3432 printCallback,
3433 #ifndef _7ZIP_ST
3434 affinityMode,
3435 #endif
3436 true, // showRating
3437 encodeRes, showFreq, cpuFreq);
3438 if (res == E_NOTIMPL)
3439 {
3440 // callback->Print(" ---");
3441 }
3442 else
3443 {
3444 RINOK(res);
3445 }
3446 callback->NewLine();
3447 }
3448 return S_OK;
3449}
3450
3451struct CTempValues
3452{
3453 UInt64 *Values;
3454 CTempValues(UInt32 num) { Values = new UInt64[num]; }
3455 ~CTempValues() { delete []Values; }
3456};
3457
3458static void ParseNumberString(const UString &s, NCOM::CPropVariant &prop)
3459{
3460 const wchar_t *end;
3461 UInt64 result = ConvertStringToUInt64(s, &end);
3462 if (*end != 0 || s.IsEmpty())
3463 prop = s;
3464 else if (result <= (UInt32)0xFFFFFFFF)
3465 prop = (UInt32)result;
3466 else
3467 prop = result;
3468}
3469
3470
3471static bool AreSameMethodNames(const char *fullName, const char *shortName)
3472{
3473 return StringsAreEqualNoCase_Ascii(fullName, shortName);
3474}
3475
3476
3477
3478
3479static void Print_Usage_and_Threads(IBenchPrintCallback &f, UInt64 usage, UInt32 threads)
3480{
3481 PrintRequirements(f, "usage:", true, usage, "Benchmark threads: ", threads);
3482}
3483
3484
3485HRESULT Bench(
3486 DECL_EXTERNAL_CODECS_LOC_VARS
3487 IBenchPrintCallback *printCallback,
3488 IBenchCallback *benchCallback,
3489 const CObjectVector<CProperty> &props,
3490 UInt32 numIterations,
3491 bool multiDict,
3492 IBenchFreqCallback *freqCallback)
3493{
3494 if (!CrcInternalTest())
3495 return E_FAIL;
3496
3497 UInt32 numCPUs = 1;
3498 UInt64 ramSize = (UInt64)(sizeof(size_t)) << 29;
3499
3500 NSystem::CProcessAffinity threadsInfo;
3501 threadsInfo.InitST();
3502
3503 #ifndef _7ZIP_ST
3504
3505 if (threadsInfo.Get() && threadsInfo.GetNumProcessThreads() != 0)
3506 numCPUs = threadsInfo.GetNumProcessThreads();
3507 else
3508 numCPUs = NSystem::GetNumberOfProcessors();
3509
3510 #endif
3511
3512 // numCPUs = 24;
3513 /*
3514 {
3515 DWORD_PTR mask = (1 << 0);
3516 DWORD_PTR old = SetThreadAffinityMask(GetCurrentThread(), mask);
3517 old = old;
3518 DWORD_PTR old2 = SetThreadAffinityMask(GetCurrentThread(), mask);
3519 old2 = old2;
3520 return 0;
3521 }
3522 */
3523
3524 bool ramSize_Defined = NSystem::GetRamSize(ramSize);
3525
3526 UInt32 numThreadsSpecified = numCPUs;
3527 bool needSetComplexity = false;
3528 UInt32 testTimeMs = kComplexInMs;
3529 UInt32 startDicLog = 22;
3530 bool startDicLog_Defined = false;
3531 UInt64 specifiedFreq = 0;
3532 bool multiThreadTests = false;
3533 UInt64 complexInCommands = kComplexInCommands;
3534 UInt32 numThreads_Start = 1;
3535
3536 #ifndef _7ZIP_ST
3537 CAffinityMode affinityMode;
3538 #endif
3539
3540
3541 COneMethodInfo method;
3542
3543 CMidAlignedBuffer fileDataBuffer;
3544 bool use_fileData = false;
3545 bool isFixedDict = false;
3546
3547 {
3548 unsigned i;
3549
3550 if (printCallback)
3551 {
3552 for (i = 0; i < props.Size(); i++)
3553 {
3554 const CProperty &property = props[i];
3555 printCallback->Print(" ");
3556 printCallback->Print(GetAnsiString(property.Name));
3557 if (!property.Value.IsEmpty())
3558 {
3559 printCallback->Print("=");
3560 printCallback->Print(GetAnsiString(property.Value));
3561 }
3562 }
3563 if (!props.IsEmpty())
3564 printCallback->NewLine();
3565 }
3566
3567
3568 for (i = 0; i < props.Size(); i++)
3569 {
3570 const CProperty &property = props[i];
3571 UString name (property.Name);
3572 name.MakeLower_Ascii();
3573
3574 if (name.IsEqualTo("file"))
3575 {
3576 if (property.Value.IsEmpty())
3577 return E_INVALIDARG;
3578
3579 NFile::NIO::CInFile file;
3580 if (!file.Open(us2fs(property.Value)))
3581 return GetLastError_noZero_HRESULT();
3582 size_t len;
3583 {
3584 UInt64 len64;
3585 if (!file.GetLength(len64))
3586 return GetLastError_noZero_HRESULT();
3587 if (printCallback)
3588 {
3589 printCallback->Print("file size =");
3590 PrintNumber(*printCallback, len64, 0);
3591 printCallback->NewLine();
3592 }
3593 len = (size_t)len64;
3594 if (len != len64)
3595 return E_INVALIDARG;
3596 }
3597
3598 // (len == 0) is allowed. Also it's allowed if Alloc(0) returns NULL here
3599
3600 ALLOC_WITH_HRESULT(&fileDataBuffer, len);
3601 use_fileData = true;
3602
3603 {
3604 size_t processed;
3605 if (!file.ReadFull((Byte *)fileDataBuffer, len, processed))
3606 return GetLastError_noZero_HRESULT();
3607 if (processed != len)
3608 return E_FAIL;
3609 }
3610 continue;
3611 }
3612
3613 NCOM::CPropVariant propVariant;
3614 if (!property.Value.IsEmpty())
3615 ParseNumberString(property.Value, propVariant);
3616
3617 if (name.IsEqualTo("time"))
3618 {
3619 RINOK(ParsePropToUInt32(UString(), propVariant, testTimeMs));
3620 needSetComplexity = true;
3621 testTimeMs *= 1000;
3622 continue;
3623 }
3624
3625 if (name.IsEqualTo("timems"))
3626 {
3627 RINOK(ParsePropToUInt32(UString(), propVariant, testTimeMs));
3628 needSetComplexity = true;
3629 continue;
3630 }
3631
3632 if (name.IsEqualTo("tic"))
3633 {
3634 UInt32 v;
3635 RINOK(ParsePropToUInt32(UString(), propVariant, v));
3636 if (v >= 64)
3637 return E_INVALIDARG;
3638 complexInCommands = (UInt64)1 << v;
3639 continue;
3640 }
3641
3642 const bool isCurrent_fixedDict = name.IsEqualTo("df");
3643 if (isCurrent_fixedDict)
3644 isFixedDict = true;
3645 if (isCurrent_fixedDict || name.IsEqualTo("ds"))
3646 {
3647 RINOK(ParsePropToUInt32(UString(), propVariant, startDicLog));
3648 if (startDicLog > 32)
3649 return E_INVALIDARG;
3650 startDicLog_Defined = true;
3651 continue;
3652 }
3653
3654 if (name.IsEqualTo("mts"))
3655 {
3656 RINOK(ParsePropToUInt32(UString(), propVariant, numThreads_Start));
3657 continue;
3658 }
3659
3660 if (name.IsEqualTo("af"))
3661 {
3662 UInt32 bundle;
3663 RINOK(ParsePropToUInt32(UString(), propVariant, bundle));
3664 if (bundle > 0 && bundle < numCPUs)
3665 {
3666 #ifndef _7ZIP_ST
3667 affinityMode.SetLevels(numCPUs, 2);
3668 affinityMode.NumBundleThreads = bundle;
3669 #endif
3670 }
3671 continue;
3672 }
3673
3674 if (name.IsEqualTo("freq"))
3675 {
3676 UInt32 freq32 = 0;
3677 RINOK(ParsePropToUInt32(UString(), propVariant, freq32));
3678 if (freq32 == 0)
3679 return E_INVALIDARG;
3680 specifiedFreq = (UInt64)freq32 * 1000000;
3681
3682 if (printCallback)
3683 {
3684 printCallback->Print("freq=");
3685 PrintNumber(*printCallback, freq32, 0);
3686 printCallback->NewLine();
3687 }
3688
3689 continue;
3690 }
3691
3692 if (name.IsPrefixedBy_Ascii_NoCase("mt"))
3693 {
3694 UString s = name.Ptr(2);
3695 if (s.IsEqualTo("*")
3696 || (s.IsEmpty()
3697 && propVariant.vt == VT_BSTR
3698 && StringsAreEqual_Ascii(propVariant.bstrVal, "*")))
3699 {
3700 multiThreadTests = true;
3701 continue;
3702 }
3703 #ifndef _7ZIP_ST
3704 RINOK(ParseMtProp(s, propVariant, numCPUs, numThreadsSpecified));
3705 #endif
3706 continue;
3707 }
3708
3709 RINOK(method.ParseMethodFromPROPVARIANT(name, propVariant));
3710 }
3711 }
3712
3713 if (printCallback)
3714 {
3715 AString s;
3716
3717 #ifndef _WIN32
3718 s += "Compiler: ";
3719 GetCompiler(s);
3720 printCallback->Print(s);
3721 printCallback->NewLine();
3722 s.Empty();
3723 #endif
3724
3725 GetSystemInfoText(s);
3726 printCallback->Print(s);
3727 printCallback->NewLine();
3728 }
3729
3730 if (printCallback)
3731 {
3732 printCallback->Print("1T CPU Freq (MHz):");
3733 }
3734
3735 if (printCallback || freqCallback)
3736 {
3737 UInt64 numMilCommands = 1 << 6;
3738 if (specifiedFreq != 0)
3739 {
3740 while (numMilCommands > 1 && specifiedFreq < (numMilCommands * 1000000))
3741 numMilCommands >>= 1;
3742 }
3743
3744 for (int jj = 0;; jj++)
3745 {
3746 if (printCallback)
3747 RINOK(printCallback->CheckBreak());
3748
3749 UInt64 start = ::GetTimeCount();
3750 UInt32 sum = (UInt32)start;
3751 sum = CountCpuFreq(sum, (UInt32)(numMilCommands * 1000000 / kNumFreqCommands), g_BenchCpuFreqTemp);
3752 if (sum == 0xF1541213)
3753 if (printCallback)
3754 printCallback->Print("");
3755 const UInt64 realDelta = ::GetTimeCount() - start;
3756 start = realDelta;
3757 if (start == 0)
3758 start = 1;
3759 if (start > (UInt64)1 << 61)
3760 start = 1;
3761 const UInt64 freq = GetFreq();
3762 // mips is constant in some compilers
3763 const UInt64 hz = MyMultDiv64(numMilCommands * 1000000, freq, start);
3764 const UInt64 mipsVal = numMilCommands * freq / start;
3765 if (printCallback)
3766 {
3767 if (realDelta == 0)
3768 {
3769 printCallback->Print(" -");
3770 }
3771 else
3772 {
3773 // PrintNumber(*printCallback, start, 0);
3774 PrintNumber(*printCallback, mipsVal, 5);
3775 }
3776 }
3777 if (freqCallback)
3778 {
3779 RINOK(freqCallback->AddCpuFreq(1, hz, kBenchmarkUsageMult));
3780 }
3781
3782 if (jj >= 1)
3783 {
3784 bool needStop = (numMilCommands >= (1 <<
3785 #ifdef _DEBUG
3786 7
3787 #else
3788 11
3789 #endif
3790 ));
3791 if (start >= freq * 16)
3792 {
3793 printCallback->Print(" (Cmplx)");
3794 if (!freqCallback) // we don't want complexity change for old gui lzma benchmark
3795 {
3796 needSetComplexity = true;
3797 }
3798 needStop = true;
3799 }
3800 if (needSetComplexity)
3801 SetComplexCommandsMs(testTimeMs, false, mipsVal * 1000000, complexInCommands);
3802 if (needStop)
3803 break;
3804 numMilCommands <<= 1;
3805 }
3806 }
3807 if (freqCallback)
3808 {
3809 RINOK(freqCallback->FreqsFinished(1));
3810 }
3811 }
3812
3813 if (numThreadsSpecified >= 2)
3814 if (printCallback || freqCallback)
3815 {
3816 if (printCallback)
3817 printCallback->NewLine();
3818
3819 /* it can show incorrect frequency for HT threads.
3820 so we reduce freq test to (numCPUs / 2) */
3821
3822 UInt32 numThreads = numThreadsSpecified >= numCPUs / 2 ? numCPUs / 2: numThreadsSpecified;
3823 if (numThreads < 1)
3824 numThreads = 1;
3825
3826 if (printCallback)
3827 {
3828 char s[128];
3829 ConvertUInt64ToString(numThreads, s);
3830 printCallback->Print(s);
3831 printCallback->Print("T CPU Freq (MHz):");
3832 }
3833 UInt64 numMilCommands = 1 << 10;
3834 if (specifiedFreq != 0)
3835 {
3836 while (numMilCommands > 1 && specifiedFreq < (numMilCommands * 1000000))
3837 numMilCommands >>= 1;
3838 }
3839
3840 for (int jj = 0;; jj++)
3841 {
3842 if (printCallback)
3843 RINOK(printCallback->CheckBreak());
3844
3845 {
3846 // PrintLeft(f, "CPU", kFieldSize_Name);
3847
3848 // UInt32 resVal;
3849
3850 CFreqBench fb;
3851 fb.complexInCommands = numMilCommands * 1000000;
3852 fb.numThreads = numThreads;
3853 // showFreq;
3854 // fb.showFreq = (freqTest == kNumCpuTests - 1 || specifiedFreq != 0);
3855 fb.showFreq = true;
3856 fb.specifiedFreq = 1;
3857
3858 HRESULT res = fb.FreqBench(NULL /* printCallback */
3859 #ifndef _7ZIP_ST
3860 , &affinityMode
3861 #endif
3862 );
3863 RINOK(res);
3864
3865 if (freqCallback)
3866 {
3867 RINOK(freqCallback->AddCpuFreq(numThreads, fb.CpuFreqRes, fb.UsageRes));
3868 }
3869
3870 if (printCallback)
3871 {
3872 /*
3873 if (realDelta == 0)
3874 {
3875 printCallback->Print(" -");
3876 }
3877 else
3878 */
3879 {
3880 // PrintNumber(*printCallback, start, 0);
3881 PrintUsage(*printCallback, fb.UsageRes, 3);
3882 printCallback->Print("%");
3883 PrintNumber(*printCallback, fb.CpuFreqRes / 1000000, 0);
3884 printCallback->Print(" ");
3885
3886 // PrintNumber(*printCallback, fb.UsageRes, 5);
3887 }
3888 }
3889 }
3890 // if (jj >= 1)
3891 {
3892 bool needStop = (numMilCommands >= (1 <<
3893 #ifdef _DEBUG
3894 7
3895 #else
3896 11
3897 #endif
3898 ));
3899 if (needStop)
3900 break;
3901 numMilCommands <<= 1;
3902 }
3903 }
3904 if (freqCallback)
3905 {
3906 RINOK(freqCallback->FreqsFinished(numThreads));
3907 }
3908 }
3909
3910
3911 if (printCallback)
3912 {
3913 printCallback->NewLine();
3914 printCallback->NewLine();
3915 PrintRequirements(*printCallback, "size: ", ramSize_Defined, ramSize, "CPU hardware threads:", numCPUs);
3916 printCallback->Print(GetProcessThreadsInfo(threadsInfo));
3917 printCallback->NewLine();
3918 }
3919
3920 if (numThreadsSpecified < 1 || numThreadsSpecified > kNumThreadsMax)
3921 return E_INVALIDARG;
3922
3923 UInt64 dict = (UInt64)1 << startDicLog;
3924 const bool dictIsDefined = (isFixedDict || method.Get_DicSize(dict));
3925
3926 const int level = method.GetLevel();
3927
3928 if (method.MethodName.IsEmpty())
3929 method.MethodName = "LZMA";
3930
3931 if (benchCallback)
3932 {
3933 CBenchProps benchProps;
3934 benchProps.SetLzmaCompexity();
3935 const UInt64 dictSize = method.Get_Lzma_DicSize();
3936
3937 size_t uncompressedDataSize;
3938 if (use_fileData)
3939 {
3940 uncompressedDataSize = fileDataBuffer.Size();
3941 }
3942 else
3943 {
3944 uncompressedDataSize = kAdditionalSize + (size_t)dictSize;
3945 if (uncompressedDataSize < dictSize)
3946 return E_INVALIDARG;
3947 }
3948
3949 return MethodBench(
3950 EXTERNAL_CODECS_LOC_VARS
3951 complexInCommands,
3952 #ifndef _7ZIP_ST
3953 true, numThreadsSpecified,
3954 &affinityMode,
3955 #endif
3956 method,
3957 uncompressedDataSize, (const Byte *)fileDataBuffer,
3958 kOldLzmaDictBits, printCallback, benchCallback, &benchProps);
3959 }
3960
3961 AString methodName (method.MethodName);
3962 if (methodName.IsEqualTo_Ascii_NoCase("CRC"))
3963 methodName = "crc32";
3964 method.MethodName = methodName;
3965 CMethodId hashID;
3966
3967 if (FindHashMethod(EXTERNAL_CODECS_LOC_VARS methodName, hashID))
3968 {
3969 if (!printCallback)
3970 return S_FALSE;
3971 IBenchPrintCallback &f = *printCallback;
3972
3973 UInt64 dict64 = dict;
3974 if (!dictIsDefined)
3975 dict64 = (1 << 27);
3976 if (use_fileData)
3977 {
3978 if (!dictIsDefined)
3979 dict64 = fileDataBuffer.Size();
3980 else if (dict64 > fileDataBuffer.Size())
3981 dict64 = fileDataBuffer.Size();
3982 }
3983
3984 // methhodName.RemoveChar(L'-');
3985 UInt32 complexity = 10000;
3986 const UInt32 *checkSum = NULL;
3987 {
3988 unsigned i;
3989 for (i = 0; i < ARRAY_SIZE(g_Hash); i++)
3990 {
3991 const CBenchHash &h = g_Hash[i];
3992 AString benchMethod (h.Name);
3993 AString benchProps;
3994 int propPos = benchMethod.Find(':');
3995 if (propPos >= 0)
3996 {
3997 benchProps = benchMethod.Ptr((unsigned)(propPos + 1));
3998 benchMethod.DeleteFrom((unsigned)propPos);
3999 }
4000
4001 if (AreSameMethodNames(benchMethod, methodName))
4002 {
4003 bool isMainMathed = method.PropsString.IsEmpty();
4004 if (isMainMathed)
4005 isMainMathed = !checkSum
4006 || (benchMethod.IsEqualTo_Ascii_NoCase("crc32") && benchProps.IsEqualTo_Ascii_NoCase("8"));
4007 const bool sameProps = method.PropsString.IsEqualTo_Ascii_NoCase(benchProps);
4008 if (sameProps || isMainMathed)
4009 {
4010 complexity = h.Complex;
4011 checkSum = &h.CheckSum;
4012 if (sameProps)
4013 break;
4014 }
4015 }
4016 }
4017 if (!checkSum)
4018 return E_NOTIMPL;
4019 }
4020
4021 {
4022 UInt64 usage = 1 << 20;
4023 UInt64 bufSize = dict64;
4024 if (use_fileData)
4025 {
4026 usage += fileDataBuffer.Size();
4027 if (bufSize > fileDataBuffer.Size())
4028 bufSize = fileDataBuffer.Size();
4029 #ifndef _7ZIP_ST
4030 if (numThreadsSpecified != 1)
4031 usage += bufSize * numThreadsSpecified * (k_Crc_CreateLocalBuf_For_File ? 1 : 0);
4032 #endif
4033 }
4034 else
4035 usage += numThreadsSpecified * bufSize;
4036 Print_Usage_and_Threads(f, usage, numThreadsSpecified);
4037 }
4038
4039 f.NewLine();
4040
4041 const unsigned kFieldSize_CrcSpeed = 7;
4042 CUIntVector numThreadsVector;
4043 {
4044 unsigned nt = numThreads_Start;
4045 for (;;)
4046 {
4047 if (nt > numThreadsSpecified)
4048 break;
4049 numThreadsVector.Add(nt);
4050 unsigned next = nt * 2;
4051 UInt32 ntHalf= numThreadsSpecified / 2;
4052 if (ntHalf > nt && ntHalf < next)
4053 numThreadsVector.Add(ntHalf);
4054 if (numThreadsSpecified > nt && numThreadsSpecified < next)
4055 numThreadsVector.Add(numThreadsSpecified);
4056 nt = next;
4057 }
4058 {
4059 f.NewLine();
4060 f.Print("THRD");
4061 FOR_VECTOR (ti, numThreadsVector)
4062 {
4063 PrintNumber(f, numThreadsVector[ti], 1 + kFieldSize_Usage + kFieldSize_CrcSpeed);
4064 }
4065 }
4066 {
4067 f.NewLine();
4068 f.Print(" ");
4069 FOR_VECTOR (ti, numThreadsVector)
4070 {
4071 PrintRight(f, "Usage", kFieldSize_Usage + 1);
4072 PrintRight(f, "BW", kFieldSize_CrcSpeed + 1);
4073 }
4074 }
4075 {
4076 f.NewLine();
4077 f.Print("Size");
4078 FOR_VECTOR (ti, numThreadsVector)
4079 {
4080 PrintRight(f, "%", kFieldSize_Usage + 1);
4081 PrintRight(f, "MB/s", kFieldSize_CrcSpeed + 1);
4082 }
4083 }
4084 }
4085
4086 f.NewLine();
4087 f.NewLine();
4088
4089 CTempValues speedTotals(numThreadsVector.Size());
4090 CTempValues usageTotals(numThreadsVector.Size());
4091 {
4092 FOR_VECTOR (ti, numThreadsVector)
4093 {
4094 speedTotals.Values[ti] = 0;
4095 usageTotals.Values[ti] = 0;
4096 }
4097 }
4098
4099 UInt64 numSteps = 0;
4100
4101 for (UInt32 i = 0; i < numIterations; i++)
4102 {
4103 unsigned pow = 10; // kNumHashDictBits
4104 if (startDicLog_Defined)
4105 pow = startDicLog;
4106 for (;; pow++)
4107 {
4108 const UInt64 bufSize = (UInt64)1 << pow;
4109 char s[16];
4110 ConvertUInt32ToString(pow, s);
4111 unsigned pos = MyStringLen(s);
4112 s[pos++] = ':';
4113 s[pos++] = ' ';
4114 s[pos] = 0;
4115 PrintRight(f, s, 4);
4116
4117 size_t dataSize = fileDataBuffer.Size();
4118 if (dataSize > bufSize || !use_fileData)
4119 dataSize = (size_t)bufSize;
4120
4121 FOR_VECTOR (ti, numThreadsVector)
4122 {
4123 RINOK(f.CheckBreak());
4124 const UInt32 t = numThreadsVector[ti];
4125 UInt64 speed = 0;
4126 UInt64 usage = 0;
4127
4128 HRESULT res = CrcBench(EXTERNAL_CODECS_LOC_VARS complexInCommands,
4129 t,
4130 dataSize, (const Byte *)fileDataBuffer,
4131 speed, usage,
4132 complexity,
4133 1, // benchWeight,
4134 (pow == kNumHashDictBits && !use_fileData) ? checkSum : NULL,
4135 method,
4136 &f,
4137 #ifndef _7ZIP_ST
4138 &affinityMode,
4139 #endif
4140 false, // showRating
4141 NULL, false, 0);
4142
4143 RINOK(res);
4144
4145 PrintUsage(f, usage, kFieldSize_Usage);
4146 PrintNumber(f, speed / 1000000, kFieldSize_CrcSpeed);
4147 speedTotals.Values[ti] += speed;
4148 usageTotals.Values[ti] += usage;
4149 }
4150
4151 f.NewLine();
4152 numSteps++;
4153 if (dataSize >= dict64)
4154 break;
4155 }
4156 }
4157 if (numSteps != 0)
4158 {
4159 f.NewLine();
4160 f.Print("Avg:");
4161 for (unsigned ti = 0; ti < numThreadsVector.Size(); ti++)
4162 {
4163 PrintUsage(f, usageTotals.Values[ti] / numSteps, kFieldSize_Usage);
4164 PrintNumber(f, speedTotals.Values[ti] / numSteps / 1000000, kFieldSize_CrcSpeed);
4165 }
4166 f.NewLine();
4167 }
4168 return S_OK;
4169 }
4170
4171 bool use2Columns = false;
4172
4173 bool totalBenchMode = (method.MethodName.IsEqualTo_Ascii_NoCase("*"));
4174 bool onlyHashBench = false;
4175 if (method.MethodName.IsEqualTo_Ascii_NoCase("hash"))
4176 {
4177 onlyHashBench = true;
4178 totalBenchMode = true;
4179 }
4180
4181 // ---------- Threads loop ----------
4182 for (unsigned threadsPassIndex = 0; threadsPassIndex < 3; threadsPassIndex++)
4183 {
4184
4185 UInt32 numThreads = numThreadsSpecified;
4186
4187 if (!multiThreadTests)
4188 {
4189 if (threadsPassIndex != 0)
4190 break;
4191 }
4192 else
4193 {
4194 numThreads = 1;
4195 if (threadsPassIndex != 0)
4196 {
4197 if (numCPUs < 2)
4198 break;
4199 numThreads = numCPUs;
4200 if (threadsPassIndex == 1)
4201 {
4202 if (numCPUs >= 4)
4203 numThreads = numCPUs / 2;
4204 }
4205 else if (numCPUs < 4)
4206 break;
4207 }
4208 }
4209
4210 CBenchCallbackToPrint callback;
4211 callback.Init();
4212 callback._file = printCallback;
4213
4214 IBenchPrintCallback &f = *printCallback;
4215
4216 if (threadsPassIndex > 0)
4217 {
4218 f.NewLine();
4219 f.NewLine();
4220 }
4221
4222 if (!dictIsDefined && !onlyHashBench)
4223 {
4224 const unsigned dicSizeLog_Main = (totalBenchMode ? 24 : 25);
4225 unsigned dicSizeLog = dicSizeLog_Main;
4226
4227 #ifdef UNDER_CE
4228 dicSizeLog = (UInt64)1 << 20;
4229 #endif
4230
4231 if (ramSize_Defined)
4232 for (; dicSizeLog > kBenchMinDicLogSize; dicSizeLog--)
4233 if (GetBenchMemoryUsage(numThreads, level, ((UInt64)1 << dicSizeLog), totalBenchMode) + (8 << 20) <= ramSize)
4234 break;
4235
4236 dict = (UInt64)1 << dicSizeLog;
4237
4238 if (totalBenchMode && dicSizeLog != dicSizeLog_Main)
4239 {
4240 f.Print("Dictionary reduced to: ");
4241 PrintNumber(f, dicSizeLog, 1);
4242 f.NewLine();
4243 }
4244 }
4245
4246 Print_Usage_and_Threads(f,
4247 onlyHashBench ?
4248 GetBenchMemoryUsage_Hash(numThreads, dict) :
4249 GetBenchMemoryUsage(numThreads, level, dict, totalBenchMode),
4250 numThreads);
4251
4252 f.NewLine();
4253
4254 f.NewLine();
4255
4256 if (totalBenchMode)
4257 {
4258 callback.NameFieldSize = kFieldSize_Name;
4259 use2Columns = false;
4260 }
4261 else
4262 {
4263 callback.NameFieldSize = kFieldSize_SmallName;
4264 use2Columns = true;
4265 }
4266 callback.Use2Columns = use2Columns;
4267
4268 bool showFreq = false;
4269 UInt64 cpuFreq = 0;
4270
4271 if (totalBenchMode)
4272 {
4273 showFreq = true;
4274 }
4275
4276 unsigned fileldSize = kFieldSize_TotalSize;
4277 if (showFreq)
4278 fileldSize += kFieldSize_EUAndEffec;
4279
4280 if (use2Columns)
4281 {
4282 PrintSpaces(f, callback.NameFieldSize);
4283 PrintRight(f, "Compressing", fileldSize);
4284 f.Print(kSep);
4285 PrintRight(f, "Decompressing", fileldSize);
4286 }
4287 f.NewLine();
4288 PrintLeft(f, totalBenchMode ? "Method" : "Dict", callback.NameFieldSize);
4289
4290 int j;
4291
4292 for (j = 0; j < 2; j++)
4293 {
4294 PrintRight(f, "Speed", kFieldSize_Speed + 1);
4295 PrintRight(f, "Usage", kFieldSize_Usage + 1);
4296 PrintRight(f, "R/U", kFieldSize_RU + 1);
4297 PrintRight(f, "Rating", kFieldSize_Rating + 1);
4298 if (showFreq)
4299 {
4300 PrintRight(f, "E/U", kFieldSize_EU + 1);
4301 PrintRight(f, "Effec", kFieldSize_Effec + 1);
4302 }
4303 if (!use2Columns)
4304 break;
4305 if (j == 0)
4306 f.Print(kSep);
4307 }
4308
4309 f.NewLine();
4310 PrintSpaces(f, callback.NameFieldSize);
4311
4312 for (j = 0; j < 2; j++)
4313 {
4314 PrintRight(f, "KiB/s", kFieldSize_Speed + 1);
4315 PrintRight(f, "%", kFieldSize_Usage + 1);
4316 PrintRight(f, "MIPS", kFieldSize_RU + 1);
4317 PrintRight(f, "MIPS", kFieldSize_Rating + 1);
4318 if (showFreq)
4319 {
4320 PrintRight(f, "%", kFieldSize_EU + 1);
4321 PrintRight(f, "%", kFieldSize_Effec + 1);
4322 }
4323 if (!use2Columns)
4324 break;
4325 if (j == 0)
4326 f.Print(kSep);
4327 }
4328
4329 f.NewLine();
4330 f.NewLine();
4331
4332 if (specifiedFreq != 0)
4333 cpuFreq = specifiedFreq;
4334
4335 // bool showTotalSpeed = false;
4336
4337 if (totalBenchMode)
4338 {
4339 for (UInt32 i = 0; i < numIterations; i++)
4340 {
4341 if (i != 0)
4342 printCallback->NewLine();
4343
4344 const unsigned kNumCpuTests = 3;
4345 for (unsigned freqTest = 0; freqTest < kNumCpuTests; freqTest++)
4346 {
4347 PrintLeft(f, "CPU", kFieldSize_Name);
4348
4349 // UInt32 resVal;
4350
4351 CFreqBench fb;
4352 fb.complexInCommands = complexInCommands;
4353 fb.numThreads = numThreads;
4354 // showFreq;
4355 fb.showFreq = (freqTest == kNumCpuTests - 1 || specifiedFreq != 0);
4356 fb.specifiedFreq = specifiedFreq;
4357
4358 HRESULT res = fb.FreqBench(printCallback
4359 #ifndef _7ZIP_ST
4360 , &affinityMode
4361 #endif
4362 );
4363 RINOK(res);
4364
4365 cpuFreq = fb.CpuFreqRes;
4366 callback.NewLine();
4367
4368 if (specifiedFreq != 0)
4369 cpuFreq = specifiedFreq;
4370
4371 if (testTimeMs >= 1000)
4372 if (freqTest == kNumCpuTests - 1)
4373 {
4374 // SetComplexCommandsMs(testTimeMs, specifiedFreq != 0, cpuFreq, complexInCommands);
4375 }
4376 }
4377 callback.NewLine();
4378
4379 // return S_OK; // change it
4380
4381 callback.SetFreq(true, cpuFreq);
4382
4383 if (!onlyHashBench)
4384 {
4385 size_t dataSize = (size_t)dict;
4386 if (use_fileData)
4387 {
4388 dataSize = fileDataBuffer.Size();
4389 if (dictIsDefined && dataSize > dict)
4390 dataSize = (size_t)dict;
4391 }
4392
4393 HRESULT res = TotalBench(EXTERNAL_CODECS_LOC_VARS
4394 complexInCommands,
4395 #ifndef _7ZIP_ST
4396 numThreads,
4397 &affinityMode,
4398 #endif
4399 dictIsDefined || use_fileData, // forceUnpackSize
4400 dataSize,
4401 (const Byte *)fileDataBuffer,
4402 printCallback, &callback);
4403 RINOK(res);
4404 }
4405
4406 {
4407 size_t dataSize = (size_t)1 << kNumHashDictBits;
4408 if (dictIsDefined)
4409 {
4410 dataSize = (size_t)dict;
4411 if (dataSize != dict)
4412 return E_OUTOFMEMORY;
4413 }
4414 if (use_fileData)
4415 {
4416 dataSize = fileDataBuffer.Size();
4417 if (dictIsDefined && dataSize > dict)
4418 dataSize = (size_t)dict;
4419 }
4420
4421 HRESULT res = TotalBench_Hash(EXTERNAL_CODECS_LOC_VARS complexInCommands, numThreads,
4422 dataSize, (const Byte *)fileDataBuffer,
4423 printCallback, &callback,
4424 #ifndef _7ZIP_ST
4425 &affinityMode,
4426 #endif
4427 &callback.EncodeRes, true, cpuFreq);
4428 RINOK(res);
4429 }
4430
4431 callback.NewLine();
4432 {
4433 PrintLeft(f, "CPU", kFieldSize_Name);
4434
4435 CFreqBench fb;
4436 fb.complexInCommands = complexInCommands;
4437 fb.numThreads = numThreads;
4438 // showFreq;
4439 fb.showFreq = (specifiedFreq != 0);
4440 fb.specifiedFreq = specifiedFreq;
4441
4442 HRESULT res = fb.FreqBench(printCallback
4443 #ifndef _7ZIP_ST
4444 , &affinityMode
4445 #endif
4446 );
4447 RINOK(res);
4448 callback.NewLine();
4449 }
4450 }
4451 }
4452 else
4453 {
4454 needSetComplexity = true;
4455 if (!methodName.IsEqualTo_Ascii_NoCase("LZMA"))
4456 {
4457 unsigned i;
4458 for (i = 0; i < ARRAY_SIZE(g_Bench); i++)
4459 {
4460 const CBenchMethod &h = g_Bench[i];
4461 AString benchMethod (h.Name);
4462 AString benchProps;
4463 int propPos = benchMethod.Find(':');
4464 if (propPos >= 0)
4465 {
4466 benchProps = benchMethod.Ptr((unsigned)(propPos + 1));
4467 benchMethod.DeleteFrom((unsigned)propPos);
4468 }
4469
4470 if (AreSameMethodNames(benchMethod, methodName))
4471 {
4472 if (benchProps.IsEmpty()
4473 || (benchProps == "x5" && method.PropsString.IsEmpty())
4474 || method.PropsString.IsPrefixedBy_Ascii_NoCase(benchProps))
4475 {
4476 callback.BenchProps.EncComplex = h.EncComplex;
4477 callback.BenchProps.DecComplexCompr = h.DecComplexCompr;
4478 callback.BenchProps.DecComplexUnc = h.DecComplexUnc;;
4479 needSetComplexity = false;
4480 break;
4481 }
4482 }
4483 }
4484 if (i == ARRAY_SIZE(g_Bench))
4485 return E_NOTIMPL;
4486 }
4487 if (needSetComplexity)
4488 callback.BenchProps.SetLzmaCompexity();
4489
4490 if (startDicLog < kBenchMinDicLogSize)
4491 startDicLog = kBenchMinDicLogSize;
4492
4493 for (unsigned i = 0; i < numIterations; i++)
4494 {
4495 unsigned pow = (dict < GetDictSizeFromLog(startDicLog)) ? kBenchMinDicLogSize : (unsigned)startDicLog;
4496 if (!multiDict)
4497 pow = 32;
4498 while (GetDictSizeFromLog(pow) > dict && pow > 0)
4499 pow--;
4500 for (; GetDictSizeFromLog(pow) <= dict; pow++)
4501 {
4502 char s[16];
4503 ConvertUInt32ToString(pow, s);
4504 unsigned pos = MyStringLen(s);
4505 s[pos++] = ':';
4506 s[pos] = 0;
4507 PrintLeft(f, s, kFieldSize_SmallName);
4508 callback.DictSize = (UInt64)1 << pow;
4509
4510 COneMethodInfo method2 = method;
4511
4512 if (StringsAreEqualNoCase_Ascii(method2.MethodName, "LZMA"))
4513 {
4514 // We add dictionary size property.
4515 // method2 can have two different dictionary size properties.
4516 // And last property is main.
4517 NCOM::CPropVariant propVariant = (UInt32)pow;
4518 RINOK(method2.ParseMethodFromPROPVARIANT((UString)"d", propVariant));
4519 }
4520
4521 size_t uncompressedDataSize;
4522 if (use_fileData)
4523 {
4524 uncompressedDataSize = fileDataBuffer.Size();
4525 }
4526 else
4527 {
4528 uncompressedDataSize = (size_t)callback.DictSize;
4529 if (uncompressedDataSize != callback.DictSize)
4530 return E_OUTOFMEMORY;
4531 if (uncompressedDataSize >= (1 << 18))
4532 uncompressedDataSize += kAdditionalSize;
4533 }
4534
4535 HRESULT res = MethodBench(
4536 EXTERNAL_CODECS_LOC_VARS
4537 complexInCommands,
4538 #ifndef _7ZIP_ST
4539 true, numThreads,
4540 &affinityMode,
4541 #endif
4542 method2,
4543 uncompressedDataSize, (const Byte *)fileDataBuffer,
4544 kOldLzmaDictBits, printCallback, &callback, &callback.BenchProps);
4545 f.NewLine();
4546 RINOK(res);
4547 if (!multiDict)
4548 break;
4549 }
4550 }
4551 }
4552
4553 PrintChars(f, '-', callback.NameFieldSize + fileldSize);
4554
4555 if (use2Columns)
4556 {
4557 f.Print(kSep);
4558 PrintChars(f, '-', fileldSize);
4559 }
4560
4561 f.NewLine();
4562
4563 if (use2Columns)
4564 {
4565 PrintLeft(f, "Avr:", callback.NameFieldSize);
4566 PrintTotals(f, showFreq, cpuFreq, !totalBenchMode, callback.EncodeRes);
4567 f.Print(kSep);
4568 PrintTotals(f, showFreq, cpuFreq, !totalBenchMode, callback.DecodeRes);
4569 f.NewLine();
4570 }
4571
4572 PrintLeft(f, "Tot:", callback.NameFieldSize);
4573 CTotalBenchRes midRes;
4574 midRes = callback.EncodeRes;
4575 midRes.Update_With_Res(callback.DecodeRes);
4576
4577 // midRes.SetSum(callback.EncodeRes, callback.DecodeRes);
4578 PrintTotals(f, showFreq, cpuFreq, false, midRes);
4579 f.NewLine();
4580
4581 }
4582 return S_OK;
4583}
diff --git a/CPP/7zip/UI/Common/Bench.h b/CPP/7zip/UI/Common/Bench.h
new file mode 100644
index 0000000..ab0c304
--- /dev/null
+++ b/CPP/7zip/UI/Common/Bench.h
@@ -0,0 +1,122 @@
1// Bench.h
2
3#ifndef __7ZIP_BENCH_H
4#define __7ZIP_BENCH_H
5
6#include "../../../Windows/System.h"
7
8#include "../../Common/CreateCoder.h"
9#include "../../UI/Common/Property.h"
10
11UInt64 Benchmark_GetUsage_Percents(UInt64 usage);
12
13struct CBenchInfo
14{
15 UInt64 GlobalTime;
16 UInt64 GlobalFreq;
17 UInt64 UserTime;
18 UInt64 UserFreq;
19 UInt64 UnpackSize;
20 UInt64 PackSize;
21 UInt64 NumIterations;
22
23 /*
24 during Code(): we track benchInfo only from one thread (theads with index[0])
25 NumIterations means number of threads
26 UnpackSize and PackSize are total sizes of all iterations of current thread
27 after Code():
28 NumIterations means the number of Iterations
29 UnpackSize and PackSize are total sizes of all threads
30 */
31
32 CBenchInfo(): NumIterations(0) {}
33
34 UInt64 GetUsage() const;
35 UInt64 GetRatingPerUsage(UInt64 rating) const;
36 UInt64 GetSpeed(UInt64 numUnits) const;
37 UInt64 GetUnpackSizeSpeed() const { return GetSpeed(UnpackSize * NumIterations); }
38
39 UInt64 Get_UnpackSize_Full() const { return UnpackSize * NumIterations; }
40
41 UInt64 GetRating_LzmaEnc(UInt64 dictSize) const;
42 UInt64 GetRating_LzmaDec() const;
43};
44
45
46struct CTotalBenchRes
47{
48 // UInt64 NumIterations1; // for Usage
49 UInt64 NumIterations2; // for Rating / RPU
50
51 UInt64 Rating;
52 UInt64 Usage;
53 UInt64 RPU;
54 UInt64 Speed;
55
56 void Init() { /* NumIterations1 = 0; */ NumIterations2 = 0; Rating = 0; Usage = 0; RPU = 0; Speed = 0; }
57
58 void SetSum(const CTotalBenchRes &r1, const CTotalBenchRes &r2)
59 {
60 Rating = (r1.Rating + r2.Rating);
61 Usage = (r1.Usage + r2.Usage);
62 RPU = (r1.RPU + r2.RPU);
63 Speed = (r1.Speed + r2.Speed);
64 // NumIterations1 = (r1.NumIterations1 + r2.NumIterations1);
65 NumIterations2 = (r1.NumIterations2 + r2.NumIterations2);
66 }
67
68 void Generate_From_BenchInfo(const CBenchInfo &info);
69 void Mult_For_Weight(unsigned weight);
70 void Update_With_Res(const CTotalBenchRes &r);
71};
72
73
74
75struct IBenchCallback
76{
77 // virtual HRESULT SetFreq(bool showFreq, UInt64 cpuFreq) = 0;
78 virtual HRESULT SetEncodeResult(const CBenchInfo &info, bool final) = 0;
79 virtual HRESULT SetDecodeResult(const CBenchInfo &info, bool final) = 0;
80};
81
82
83
84const unsigned kBenchMinDicLogSize = 18;
85
86UInt64 GetBenchMemoryUsage(UInt32 numThreads, int level, UInt64 dictionary, bool totalBench);
87
88struct IBenchPrintCallback
89{
90 virtual void Print(const char *s) = 0;
91 virtual void NewLine() = 0;
92 virtual HRESULT CheckBreak() = 0;
93};
94
95struct IBenchFreqCallback
96{
97 virtual HRESULT AddCpuFreq(unsigned numThreads, UInt64 freq, UInt64 usage) = 0;
98 virtual HRESULT FreqsFinished(unsigned numThreads) = 0;
99};
100
101HRESULT Bench(
102 DECL_EXTERNAL_CODECS_LOC_VARS
103 IBenchPrintCallback *printCallback,
104 IBenchCallback *benchCallback,
105 const CObjectVector<CProperty> &props,
106 UInt32 numIterations,
107 bool multiDict,
108 IBenchFreqCallback *freqCallback = NULL);
109
110AString GetProcessThreadsInfo(const NWindows::NSystem::CProcessAffinity &ti);
111
112void GetSysInfo(AString &s1, AString &s2);
113void GetCpuName(AString &s);
114void AddCpuFeatures(AString &s);
115
116#ifdef _7ZIP_LARGE_PAGES
117void Add_LargePages_String(AString &s);
118#else
119// #define Add_LargePages_String
120#endif
121
122#endif
diff --git a/CPP/7zip/UI/Common/CompressCall.cpp b/CPP/7zip/UI/Common/CompressCall.cpp
new file mode 100644
index 0000000..c7efa99
--- /dev/null
+++ b/CPP/7zip/UI/Common/CompressCall.cpp
@@ -0,0 +1,338 @@
1// CompressCall.cpp
2
3#include "StdAfx.h"
4
5#include <wchar.h>
6
7#include "../../../Common/IntToString.h"
8#include "../../../Common/MyCom.h"
9#include "../../../Common/Random.h"
10#include "../../../Common/StringConvert.h"
11
12#include "../../../Windows/DLL.h"
13#include "../../../Windows/ErrorMsg.h"
14#include "../../../Windows/FileDir.h"
15#include "../../../Windows/FileMapping.h"
16#include "../../../Windows/MemoryLock.h"
17#include "../../../Windows/ProcessUtils.h"
18#include "../../../Windows/Synchronization.h"
19
20#include "../FileManager/RegistryUtils.h"
21
22#include "CompressCall.h"
23
24using namespace NWindows;
25
26#define MY_TRY_BEGIN try {
27
28#define MY_TRY_FINISH } \
29 catch(...) { ErrorMessageHRESULT(E_FAIL); return E_FAIL; }
30
31#define MY_TRY_FINISH_VOID } \
32 catch(...) { ErrorMessageHRESULT(E_FAIL); }
33
34#define k7zGui "7zG.exe"
35
36// 21.07 : we can disable wildcard
37// #define ISWITCH_NO_WILDCARD_POSTFIX "w-"
38#define ISWITCH_NO_WILDCARD_POSTFIX
39
40#define kShowDialogSwitch " -ad"
41#define kEmailSwitch " -seml."
42#define kArchiveTypeSwitch " -t"
43#define kIncludeSwitch " -i" ISWITCH_NO_WILDCARD_POSTFIX
44#define kArcIncludeSwitches " -an -ai" ISWITCH_NO_WILDCARD_POSTFIX
45#define kHashIncludeSwitches kIncludeSwitch
46#define kStopSwitchParsing " --"
47
48extern HWND g_HWND;
49
50UString GetQuotedString(const UString &s)
51{
52 UString s2 ('\"');
53 s2 += s;
54 s2 += '\"';
55 return s2;
56}
57
58static void ErrorMessage(LPCWSTR message)
59{
60 MessageBoxW(g_HWND, message, L"7-Zip", MB_ICONERROR | MB_OK);
61}
62
63static void ErrorMessageHRESULT(HRESULT res, LPCWSTR s = NULL)
64{
65 UString s2 = NError::MyFormatMessage(res);
66 if (s)
67 {
68 s2.Add_LF();
69 s2 += s;
70 }
71 ErrorMessage(s2);
72}
73
74static HRESULT Call7zGui(const UString &params,
75 // LPCWSTR curDir,
76 bool waitFinish,
77 NSynchronization::CBaseEvent *event)
78{
79 UString imageName = fs2us(NWindows::NDLL::GetModuleDirPrefix());
80 imageName += k7zGui;
81
82 CProcess process;
83 const WRes wres = process.Create(imageName, params, NULL); // curDir);
84 if (wres != 0)
85 {
86 HRESULT hres = HRESULT_FROM_WIN32(wres);
87 ErrorMessageHRESULT(hres, imageName);
88 return hres;
89 }
90 if (waitFinish)
91 process.Wait();
92 else if (event != NULL)
93 {
94 HANDLE handles[] = { process, *event };
95 ::WaitForMultipleObjects(ARRAY_SIZE(handles), handles, FALSE, INFINITE);
96 }
97 return S_OK;
98}
99
100static void AddLagePagesSwitch(UString &params)
101{
102 if (ReadLockMemoryEnable())
103 #ifndef UNDER_CE
104 if (NSecurity::Get_LargePages_RiskLevel() == 0)
105 #endif
106 params += " -slp";
107}
108
109class CRandNameGenerator
110{
111 CRandom _random;
112public:
113 CRandNameGenerator() { _random.Init(); }
114 void GenerateName(UString &s, const char *prefix)
115 {
116 s += prefix;
117 s.Add_UInt32((UInt32)(unsigned)_random.Generate());
118 }
119};
120
121static HRESULT CreateMap(const UStringVector &names,
122 CFileMapping &fileMapping, NSynchronization::CManualResetEvent &event,
123 UString &params)
124{
125 size_t totalSize = 1;
126 {
127 FOR_VECTOR (i, names)
128 totalSize += (names[i].Len() + 1);
129 }
130 totalSize *= sizeof(wchar_t);
131
132 CRandNameGenerator random;
133
134 UString mappingName;
135 for (;;)
136 {
137 random.GenerateName(mappingName, "7zMap");
138 const WRes wres = fileMapping.Create(PAGE_READWRITE, totalSize, GetSystemString(mappingName));
139 if (fileMapping.IsCreated() && wres == 0)
140 break;
141 if (wres != ERROR_ALREADY_EXISTS)
142 return HRESULT_FROM_WIN32(wres);
143 fileMapping.Close();
144 }
145
146 UString eventName;
147 for (;;)
148 {
149 random.GenerateName(eventName, "7zEvent");
150 const WRes wres = event.CreateWithName(false, GetSystemString(eventName));
151 if (event.IsCreated() && wres == 0)
152 break;
153 if (wres != ERROR_ALREADY_EXISTS)
154 return HRESULT_FROM_WIN32(wres);
155 event.Close();
156 }
157
158 params += '#';
159 params += mappingName;
160 params += ':';
161 char temp[32];
162 ConvertUInt64ToString(totalSize, temp);
163 params += temp;
164
165 params += ':';
166 params += eventName;
167
168 LPVOID data = fileMapping.Map(FILE_MAP_WRITE, 0, totalSize);
169 if (!data)
170 return E_FAIL;
171 CFileUnmapper unmapper(data);
172 {
173 wchar_t *cur = (wchar_t *)data;
174 *cur++ = 0; // it means wchar_t strings (UTF-16 in WIN32)
175 FOR_VECTOR (i, names)
176 {
177 const UString &s = names[i];
178 unsigned len = s.Len() + 1;
179 wmemcpy(cur, (const wchar_t *)s, len);
180 cur += len;
181 }
182 }
183 return S_OK;
184}
185
186HRESULT CompressFiles(
187 const UString &arcPathPrefix,
188 const UString &arcName,
189 const UString &arcType,
190 bool addExtension,
191 const UStringVector &names,
192 bool email, bool showDialog, bool waitFinish)
193{
194 MY_TRY_BEGIN
195 UString params ('a');
196
197 CFileMapping fileMapping;
198 NSynchronization::CManualResetEvent event;
199 params += kIncludeSwitch;
200 RINOK(CreateMap(names, fileMapping, event, params));
201
202 if (!arcType.IsEmpty())
203 {
204 params += kArchiveTypeSwitch;
205 params += arcType;
206 }
207
208 if (email)
209 params += kEmailSwitch;
210
211 if (showDialog)
212 params += kShowDialogSwitch;
213
214 AddLagePagesSwitch(params);
215
216 if (arcName.IsEmpty())
217 params += " -an";
218
219 if (addExtension)
220 params += " -saa";
221 else
222 params += " -sae";
223
224 params += kStopSwitchParsing;
225 params.Add_Space();
226
227 if (!arcName.IsEmpty())
228 {
229 params += GetQuotedString(
230 // #ifdef UNDER_CE
231 arcPathPrefix +
232 // #endif
233 arcName);
234 }
235
236 return Call7zGui(params,
237 // (arcPathPrefix.IsEmpty()? 0: (LPCWSTR)arcPathPrefix),
238 waitFinish, &event);
239 MY_TRY_FINISH
240}
241
242static void ExtractGroupCommand(const UStringVector &arcPaths, UString &params, bool isHash)
243{
244 AddLagePagesSwitch(params);
245 params += (isHash ? kHashIncludeSwitches : kArcIncludeSwitches);
246 CFileMapping fileMapping;
247 NSynchronization::CManualResetEvent event;
248 HRESULT result = CreateMap(arcPaths, fileMapping, event, params);
249 if (result == S_OK)
250 result = Call7zGui(params, false, &event);
251 if (result != S_OK)
252 ErrorMessageHRESULT(result);
253}
254
255void ExtractArchives(const UStringVector &arcPaths, const UString &outFolder, bool showDialog, bool elimDup)
256{
257 MY_TRY_BEGIN
258 UString params ('x');
259 if (!outFolder.IsEmpty())
260 {
261 params += " -o";
262 params += GetQuotedString(outFolder);
263 }
264 if (elimDup)
265 params += " -spe";
266 if (showDialog)
267 params += kShowDialogSwitch;
268 ExtractGroupCommand(arcPaths, params, false);
269 MY_TRY_FINISH_VOID
270}
271
272
273void TestArchives(const UStringVector &arcPaths, bool hashMode)
274{
275 MY_TRY_BEGIN
276 UString params ('t');
277 if (hashMode)
278 {
279 params += kArchiveTypeSwitch;
280 params += "hash";
281 }
282 ExtractGroupCommand(arcPaths, params, false);
283 MY_TRY_FINISH_VOID
284}
285
286
287void CalcChecksum(const UStringVector &paths,
288 const UString &methodName,
289 const UString &arcPathPrefix,
290 const UString &arcFileName)
291{
292 MY_TRY_BEGIN
293
294 if (!arcFileName.IsEmpty())
295 {
296 CompressFiles(
297 arcPathPrefix,
298 arcFileName,
299 UString("hash"),
300 false, // addExtension,
301 paths,
302 false, // email,
303 false, // showDialog,
304 false // waitFinish
305 );
306 return;
307 }
308
309 UString params ('h');
310 if (!methodName.IsEmpty())
311 {
312 params += " -scrc";
313 params += methodName;
314 /*
315 if (!arcFileName.IsEmpty())
316 {
317 // not used alternate method of generating file
318 params += " -scrf=";
319 params += GetQuotedString(arcPathPrefix + arcFileName);
320 }
321 */
322 }
323 ExtractGroupCommand(paths, params, true);
324 MY_TRY_FINISH_VOID
325}
326
327void Benchmark(bool totalMode)
328{
329 MY_TRY_BEGIN
330 UString params ('b');
331 if (totalMode)
332 params += " -mm=*";
333 AddLagePagesSwitch(params);
334 HRESULT result = Call7zGui(params, false, NULL);
335 if (result != S_OK)
336 ErrorMessageHRESULT(result);
337 MY_TRY_FINISH_VOID
338}
diff --git a/CPP/7zip/UI/Common/CompressCall.h b/CPP/7zip/UI/Common/CompressCall.h
new file mode 100644
index 0000000..b817df0
--- /dev/null
+++ b/CPP/7zip/UI/Common/CompressCall.h
@@ -0,0 +1,28 @@
1// CompressCall.h
2
3#ifndef __COMPRESS_CALL_H
4#define __COMPRESS_CALL_H
5
6#include "../../../Common/MyString.h"
7
8UString GetQuotedString(const UString &s);
9
10HRESULT CompressFiles(
11 const UString &arcPathPrefix,
12 const UString &arcName,
13 const UString &arcType,
14 bool addExtension,
15 const UStringVector &names,
16 bool email, bool showDialog, bool waitFinish);
17
18void ExtractArchives(const UStringVector &arcPaths, const UString &outFolder, bool showDialog, bool elimDup);
19void TestArchives(const UStringVector &arcPaths, bool hashMode = false);
20
21void CalcChecksum(const UStringVector &paths,
22 const UString &methodName,
23 const UString &arcPathPrefix,
24 const UString &arcFileName);
25
26void Benchmark(bool totalMode);
27
28#endif
diff --git a/CPP/7zip/UI/Common/CompressCall2.cpp b/CPP/7zip/UI/Common/CompressCall2.cpp
new file mode 100644
index 0000000..762342d
--- /dev/null
+++ b/CPP/7zip/UI/Common/CompressCall2.cpp
@@ -0,0 +1,319 @@
1// CompressCall2.cpp
2
3#include "StdAfx.h"
4
5#include "../../../Common/MyException.h"
6
7#include "../../UI/Common/EnumDirItems.h"
8
9#include "../../UI/FileManager/LangUtils.h"
10
11#include "../../UI/GUI/BenchmarkDialog.h"
12#include "../../UI/GUI/ExtractGUI.h"
13#include "../../UI/GUI/UpdateGUI.h"
14#include "../../UI/GUI/HashGUI.h"
15
16#include "../../UI/GUI/ExtractRes.h"
17
18#include "CompressCall.h"
19
20extern HWND g_HWND;
21
22#define MY_TRY_BEGIN HRESULT result; try {
23#define MY_TRY_FINISH } \
24 catch(CSystemException &e) { result = e.ErrorCode; } \
25 catch(UString &s) { ErrorMessage(s); result = E_FAIL; } \
26 catch(...) { result = E_FAIL; } \
27 if (result != S_OK && result != E_ABORT) \
28 ErrorMessageHRESULT(result);
29
30static void ThrowException_if_Error(HRESULT res)
31{
32 if (res != S_OK)
33 throw CSystemException(res);
34}
35
36#ifdef EXTERNAL_CODECS
37
38#define CREATE_CODECS \
39 CCodecs *codecs = new CCodecs; \
40 CMyComPtr<ICompressCodecsInfo> compressCodecsInfo = codecs; \
41 ThrowException_if_Error(codecs->Load()); \
42 Codecs_AddHashArcHandler(codecs);
43
44#define LOAD_EXTERNAL_CODECS \
45 CExternalCodecs __externalCodecs; \
46 __externalCodecs.GetCodecs = codecs; \
47 __externalCodecs.GetHashers = codecs; \
48 ThrowException_if_Error(__externalCodecs.Load());
49
50#else
51
52#define CREATE_CODECS \
53 CCodecs *codecs = new CCodecs; \
54 CMyComPtr<IUnknown> compressCodecsInfo = codecs; \
55 ThrowException_if_Error(codecs->Load()); \
56 Codecs_AddHashArcHandler(codecs);
57
58#define LOAD_EXTERNAL_CODECS
59
60#endif
61
62
63
64
65UString GetQuotedString(const UString &s)
66{
67 UString s2 ('\"');
68 s2 += s;
69 s2 += '\"';
70 return s2;
71}
72
73static void ErrorMessage(LPCWSTR message)
74{
75 MessageBoxW(g_HWND, message, L"7-Zip", MB_ICONERROR);
76}
77
78static void ErrorMessageHRESULT(HRESULT res)
79{
80 ErrorMessage(HResultToMessage(res));
81}
82
83static void ErrorLangMessage(UINT resourceID)
84{
85 ErrorMessage(LangString(resourceID));
86}
87
88HRESULT CompressFiles(
89 const UString &arcPathPrefix,
90 const UString &arcName,
91 const UString &arcType,
92 bool addExtension,
93 const UStringVector &names,
94 bool email, bool showDialog, bool /* waitFinish */)
95{
96 MY_TRY_BEGIN
97
98 CREATE_CODECS
99
100 CUpdateCallbackGUI callback;
101
102 callback.Init();
103
104 CUpdateOptions uo;
105 uo.EMailMode = email;
106 uo.SetActionCommand_Add();
107
108 uo.ArcNameMode = (addExtension ? k_ArcNameMode_Add : k_ArcNameMode_Exact);
109
110 CObjectVector<COpenType> formatIndices;
111 if (!ParseOpenTypes(*codecs, arcType, formatIndices))
112 {
113 ErrorLangMessage(IDS_UNSUPPORTED_ARCHIVE_TYPE);
114 return E_FAIL;
115 }
116 const UString arcPath = arcPathPrefix + arcName;
117 if (!uo.InitFormatIndex(codecs, formatIndices, arcPath) ||
118 !uo.SetArcPath(codecs, arcPath))
119 {
120 ErrorLangMessage(IDS_UPDATE_NOT_SUPPORTED);
121 return E_FAIL;
122 }
123
124 NWildcard::CCensor censor;
125 FOR_VECTOR (i, names)
126 {
127 censor.AddPreItem_NoWildcard(names[i]);
128 }
129
130 bool messageWasDisplayed = false;
131
132 result = UpdateGUI(codecs,
133 formatIndices, arcPath,
134 censor, uo, showDialog, messageWasDisplayed, &callback, g_HWND);
135
136 if (result != S_OK)
137 {
138 if (result != E_ABORT && messageWasDisplayed)
139 return E_FAIL;
140 throw CSystemException(result);
141 }
142 if (callback.FailedFiles.Size() > 0)
143 {
144 if (!messageWasDisplayed)
145 throw CSystemException(E_FAIL);
146 return E_FAIL;
147 }
148
149 MY_TRY_FINISH
150 return S_OK;
151}
152
153
154static HRESULT ExtractGroupCommand(const UStringVector &arcPaths,
155 bool showDialog, const UString &outFolder, bool testMode, bool elimDup = false,
156 const char *kType = NULL)
157{
158 MY_TRY_BEGIN
159
160 CREATE_CODECS
161
162 CExtractOptions eo;
163 eo.OutputDir = us2fs(outFolder);
164 eo.TestMode = testMode;
165 eo.ElimDup.Val = elimDup;
166 eo.ElimDup.Def = elimDup;
167
168 CExtractCallbackImp *ecs = new CExtractCallbackImp;
169 CMyComPtr<IFolderArchiveExtractCallback> extractCallback = ecs;
170
171 ecs->Init();
172
173 // eo.CalcCrc = options.CalcCrc;
174
175 UStringVector arcPathsSorted;
176 UStringVector arcFullPathsSorted;
177 {
178 NWildcard::CCensor arcCensor;
179 FOR_VECTOR (i, arcPaths)
180 {
181 arcCensor.AddPreItem_NoWildcard(arcPaths[i]);
182 }
183 arcCensor.AddPathsToCensor(NWildcard::k_RelatPath);
184 CDirItemsStat st;
185 EnumerateDirItemsAndSort(arcCensor, NWildcard::k_RelatPath, UString(),
186 arcPathsSorted, arcFullPathsSorted,
187 st,
188 NULL // &scan: change it!!!!
189 );
190 }
191
192 CObjectVector<COpenType> formatIndices;
193 if (kType)
194 {
195 if (!ParseOpenTypes(*codecs, UString(kType), formatIndices))
196 {
197 throw CSystemException(E_INVALIDARG);
198 // ErrorLangMessage(IDS_UNSUPPORTED_ARCHIVE_TYPE);
199 // return E_INVALIDARG;
200 }
201 }
202
203 NWildcard::CCensor censor;
204 {
205 censor.AddPreItem_Wildcard();
206 }
207
208 censor.AddPathsToCensor(NWildcard::k_RelatPath);
209
210 bool messageWasDisplayed = false;
211
212 ecs->MultiArcMode = (arcPathsSorted.Size() > 1);
213
214 result = ExtractGUI(codecs,
215 formatIndices, CIntVector(),
216 arcPathsSorted, arcFullPathsSorted,
217 censor.Pairs.Front().Head, eo, NULL, showDialog, messageWasDisplayed, ecs, g_HWND);
218
219 if (result != S_OK)
220 {
221 if (result != E_ABORT && messageWasDisplayed)
222 return E_FAIL;
223 throw CSystemException(result);
224 }
225 return ecs->IsOK() ? S_OK : E_FAIL;
226
227 MY_TRY_FINISH
228 return result;
229}
230
231void ExtractArchives(const UStringVector &arcPaths, const UString &outFolder, bool showDialog, bool elimDup)
232{
233 ExtractGroupCommand(arcPaths, showDialog, outFolder, false, elimDup);
234}
235
236void TestArchives(const UStringVector &arcPaths, bool hashMode)
237{
238 ExtractGroupCommand(arcPaths, true, UString(), true,
239 false, // elimDup
240 hashMode ? "hash" : NULL);
241}
242
243void CalcChecksum(const UStringVector &paths,
244 const UString &methodName,
245 const UString &arcPathPrefix,
246 const UString &arcFileName)
247{
248 MY_TRY_BEGIN
249
250 if (!arcFileName.IsEmpty())
251 {
252 CompressFiles(
253 arcPathPrefix,
254 arcFileName,
255 UString("hash"),
256 false, // addExtension,
257 paths,
258 false, // email,
259 false, // showDialog,
260 false // waitFinish
261 );
262 return;
263 }
264
265 CREATE_CODECS
266 LOAD_EXTERNAL_CODECS
267
268 NWildcard::CCensor censor;
269 FOR_VECTOR (i, paths)
270 {
271 censor.AddPreItem_NoWildcard(paths[i]);
272 }
273
274 censor.AddPathsToCensor(NWildcard::k_RelatPath);
275 bool messageWasDisplayed = false;
276
277 CHashOptions options;
278 options.Methods.Add(methodName);
279
280 /*
281 if (!arcFileName.IsEmpty())
282 options.HashFilePath = arcPathPrefix + arcFileName;
283 */
284
285 result = HashCalcGUI(EXTERNAL_CODECS_VARS_L censor, options, messageWasDisplayed);
286 if (result != S_OK)
287 {
288 if (result != E_ABORT && messageWasDisplayed)
289 return; // E_FAIL;
290 throw CSystemException(result);
291 }
292
293 MY_TRY_FINISH
294 return; // result;
295}
296
297void Benchmark(bool totalMode)
298{
299 MY_TRY_BEGIN
300
301 CREATE_CODECS
302 LOAD_EXTERNAL_CODECS
303
304 CObjectVector<CProperty> props;
305 if (totalMode)
306 {
307 CProperty prop;
308 prop.Name = "m";
309 prop.Value = "*";
310 props.Add(prop);
311 }
312 result = Benchmark(
313 EXTERNAL_CODECS_VARS_L
314 props,
315 k_NumBenchIterations_Default,
316 g_HWND);
317
318 MY_TRY_FINISH
319}
diff --git a/CPP/7zip/UI/Common/DefaultName.cpp b/CPP/7zip/UI/Common/DefaultName.cpp
new file mode 100644
index 0000000..8c34ffc
--- /dev/null
+++ b/CPP/7zip/UI/Common/DefaultName.cpp
@@ -0,0 +1,37 @@
1// DefaultName.cpp
2
3#include "StdAfx.h"
4
5#include "DefaultName.h"
6
7static UString GetDefaultName3(const UString &fileName,
8 const UString &extension, const UString &addSubExtension)
9{
10 const unsigned extLen = extension.Len();
11 const unsigned fileNameLen = fileName.Len();
12
13 if (fileNameLen > extLen + 1)
14 {
15 const unsigned dotPos = fileNameLen - (extLen + 1);
16 if (fileName[dotPos] == '.')
17 if (extension.IsEqualTo_NoCase(fileName.Ptr(dotPos + 1)))
18 return fileName.Left(dotPos) + addSubExtension;
19 }
20
21 int dotPos = fileName.ReverseFind_Dot();
22 if (dotPos > 0)
23 return fileName.Left((unsigned)dotPos) + addSubExtension;
24
25 if (addSubExtension.IsEmpty())
26 return fileName + L'~';
27 else
28 return fileName + addSubExtension;
29}
30
31UString GetDefaultName2(const UString &fileName,
32 const UString &extension, const UString &addSubExtension)
33{
34 UString name = GetDefaultName3(fileName, extension, addSubExtension);
35 name.TrimRight();
36 return name;
37}
diff --git a/CPP/7zip/UI/Common/DefaultName.h b/CPP/7zip/UI/Common/DefaultName.h
new file mode 100644
index 0000000..df16456
--- /dev/null
+++ b/CPP/7zip/UI/Common/DefaultName.h
@@ -0,0 +1,11 @@
1// DefaultName.h
2
3#ifndef __DEFAULT_NAME_H
4#define __DEFAULT_NAME_H
5
6#include "../../../Common/MyString.h"
7
8UString GetDefaultName2(const UString &fileName,
9 const UString &extension, const UString &addSubExtension);
10
11#endif
diff --git a/CPP/7zip/UI/Common/DirItem.h b/CPP/7zip/UI/Common/DirItem.h
new file mode 100644
index 0000000..e5f578d
--- /dev/null
+++ b/CPP/7zip/UI/Common/DirItem.h
@@ -0,0 +1,223 @@
1// DirItem.h
2
3#ifndef __DIR_ITEM_H
4#define __DIR_ITEM_H
5
6#ifdef _WIN32
7#include "../../../Common/MyLinux.h"
8#endif
9
10#include "../../../Common/MyString.h"
11
12#include "../../../Windows/FileFind.h"
13
14#include "../../Common/UniqBlocks.h"
15
16#include "../../Archive/IArchive.h"
17
18struct CDirItemsStat
19{
20 UInt64 NumDirs;
21 UInt64 NumFiles;
22 UInt64 NumAltStreams;
23 UInt64 FilesSize;
24 UInt64 AltStreamsSize;
25
26 UInt64 NumErrors;
27
28 // UInt64 Get_NumItems() const { return NumDirs + NumFiles + NumAltStreams; }
29 UInt64 Get_NumDataItems() const { return NumFiles + NumAltStreams; }
30 UInt64 GetTotalBytes() const { return FilesSize + AltStreamsSize; }
31
32 bool IsEmpty() const { return
33 0 == NumDirs
34 && 0 == NumFiles
35 && 0 == NumAltStreams
36 && 0 == FilesSize
37 && 0 == AltStreamsSize
38 && 0 == NumErrors; }
39
40 CDirItemsStat():
41 NumDirs(0),
42 NumFiles(0),
43 NumAltStreams(0),
44 FilesSize(0),
45 AltStreamsSize(0),
46 NumErrors(0)
47 {}
48};
49
50
51struct CDirItemsStat2: public CDirItemsStat
52{
53 UInt64 Anti_NumDirs;
54 UInt64 Anti_NumFiles;
55 UInt64 Anti_NumAltStreams;
56
57 // UInt64 Get_NumItems() const { return Anti_NumDirs + Anti_NumFiles + Anti_NumAltStreams + CDirItemsStat::Get_NumItems(); }
58 UInt64 Get_NumDataItems2() const { return Anti_NumFiles + Anti_NumAltStreams + CDirItemsStat::Get_NumDataItems(); }
59
60 bool IsEmpty() const { return CDirItemsStat::IsEmpty()
61 && 0 == Anti_NumDirs
62 && 0 == Anti_NumFiles
63 && 0 == Anti_NumAltStreams; }
64
65 CDirItemsStat2():
66 Anti_NumDirs(0),
67 Anti_NumFiles(0),
68 Anti_NumAltStreams(0)
69 {}
70};
71
72
73
74#define INTERFACE_IDirItemsCallback(x) \
75 virtual HRESULT ScanError(const FString &path, DWORD systemError) x; \
76 virtual HRESULT ScanProgress(const CDirItemsStat &st, const FString &path, bool isDir) x; \
77
78struct IDirItemsCallback
79{
80 INTERFACE_IDirItemsCallback(=0)
81};
82
83struct CDirItem
84{
85 UInt64 Size;
86 FILETIME CTime;
87 FILETIME ATime;
88 FILETIME MTime;
89 UString Name;
90
91 #ifndef UNDER_CE
92 CByteBuffer ReparseData;
93
94 #ifdef _WIN32
95 // UString ShortName;
96 CByteBuffer ReparseData2; // fixed (reduced) absolute links for WIM format
97 bool AreReparseData() const { return ReparseData.Size() != 0 || ReparseData2.Size() != 0; }
98 #else
99 bool AreReparseData() const { return ReparseData.Size() != 0; }
100 #endif // _WIN32
101
102 #endif // !UNDER_CE
103
104 UInt32 Attrib;
105 int PhyParent;
106 int LogParent;
107 int SecureIndex;
108
109 bool IsAltStream;
110
111 CDirItem(): PhyParent(-1), LogParent(-1), SecureIndex(-1), IsAltStream(false) {}
112
113 bool IsDir() const { return (Attrib & FILE_ATTRIBUTE_DIRECTORY) != 0; }
114 bool IsReadOnly() const { return (Attrib & FILE_ATTRIBUTE_READONLY) != 0; }
115 bool Has_Attrib_ReparsePoint() const { return (Attrib & FILE_ATTRIBUTE_REPARSE_POINT) != 0; }
116
117 #ifdef _WIN32
118 UInt32 GetPosixAttrib() const
119 {
120 UInt32 v = IsDir() ? MY_LIN_S_IFDIR : MY_LIN_S_IFREG;
121 /* 21.06: as WSL we allow write permissions (0222) for directories even for (FILE_ATTRIBUTE_READONLY).
122 So extracting at Linux will be allowed to write files inside (0777) directories. */
123 v |= ((IsReadOnly() && !IsDir()) ? 0555 : 0777);
124 return v;
125 }
126 #endif
127};
128
129
130
131class CDirItems
132{
133 UStringVector Prefixes;
134 CIntVector PhyParents;
135 CIntVector LogParents;
136
137 UString GetPrefixesPath(const CIntVector &parents, int index, const UString &name) const;
138
139 HRESULT EnumerateDir(int phyParent, int logParent, const FString &phyPrefix);
140
141public:
142 CObjectVector<CDirItem> Items;
143
144 bool SymLinks;
145 bool ScanAltStreams;
146 bool ExcludeDirItems;
147 bool ExcludeFileItems;
148
149 /* it must be called after anotrher checks */
150 bool CanIncludeItem(bool isDir) const
151 {
152 return isDir ? !ExcludeDirItems : !ExcludeFileItems;
153 }
154
155
156 CDirItemsStat Stat;
157
158 #if !defined(UNDER_CE)
159 HRESULT SetLinkInfo(CDirItem &dirItem, const NWindows::NFile::NFind::CFileInfo &fi,
160 const FString &phyPrefix);
161 #endif
162
163 #if defined(_WIN32) && !defined(UNDER_CE)
164
165 CUniqBlocks SecureBlocks;
166 CByteBuffer TempSecureBuf;
167 bool _saclEnabled;
168 bool ReadSecure;
169
170 HRESULT AddSecurityItem(const FString &path, int &secureIndex);
171 HRESULT FillFixedReparse();
172
173 #endif
174
175 IDirItemsCallback *Callback;
176
177 CDirItems();
178
179 void AddDirFileInfo(int phyParent, int logParent, int secureIndex,
180 const NWindows::NFile::NFind::CFileInfo &fi);
181
182 HRESULT AddError(const FString &path, DWORD errorCode);
183 HRESULT AddError(const FString &path);
184
185 HRESULT ScanProgress(const FString &path);
186
187 // unsigned GetNumFolders() const { return Prefixes.Size(); }
188 FString GetPhyPath(unsigned index) const;
189 UString GetLogPath(unsigned index) const;
190
191 unsigned AddPrefix(int phyParent, int logParent, const UString &prefix);
192 void DeleteLastPrefix();
193
194 // HRESULT EnumerateOneDir(const FString &phyPrefix, CObjectVector<NWindows::NFile::NFind::CDirEntry> &files);
195 HRESULT EnumerateOneDir(const FString &phyPrefix, CObjectVector<NWindows::NFile::NFind::CFileInfo> &files);
196
197 HRESULT EnumerateItems2(
198 const FString &phyPrefix,
199 const UString &logPrefix,
200 const FStringVector &filePaths,
201 FStringVector *requestedPaths);
202
203 void ReserveDown();
204};
205
206
207struct CArcItem
208{
209 UInt64 Size;
210 FILETIME MTime;
211 UString Name;
212 bool IsDir;
213 bool IsAltStream;
214 bool SizeDefined;
215 bool MTimeDefined;
216 bool Censored;
217 UInt32 IndexInServer;
218 int TimeType;
219
220 CArcItem(): IsDir(false), IsAltStream(false), SizeDefined(false), MTimeDefined(false), Censored(false), TimeType(-1) {}
221};
222
223#endif
diff --git a/CPP/7zip/UI/Common/EnumDirItems.cpp b/CPP/7zip/UI/Common/EnumDirItems.cpp
new file mode 100644
index 0000000..89cce2b
--- /dev/null
+++ b/CPP/7zip/UI/Common/EnumDirItems.cpp
@@ -0,0 +1,1449 @@
1// EnumDirItems.cpp
2
3#include "StdAfx.h"
4
5#include <wchar.h>
6// #include <stdio.h>
7
8#include "../../../Common/Wildcard.h"
9
10#include "../../../Windows/FileDir.h"
11#include "../../../Windows/FileIO.h"
12#include "../../../Windows/FileName.h"
13
14#if defined(_WIN32) && !defined(UNDER_CE)
15#define _USE_SECURITY_CODE
16#include "../../../Windows/SecurityUtils.h"
17#endif
18
19#include "EnumDirItems.h"
20#include "SortUtils.h"
21
22using namespace NWindows;
23using namespace NFile;
24using namespace NName;
25
26void CDirItems::AddDirFileInfo(int phyParent, int logParent, int secureIndex,
27 const NFind::CFileInfo &fi)
28{
29 CDirItem di;
30 di.Size = fi.Size;
31 di.CTime = fi.CTime;
32 di.ATime = fi.ATime;
33 di.MTime = fi.MTime;
34 di.Attrib = fi.Attrib;
35 di.IsAltStream = fi.IsAltStream;
36 di.PhyParent = phyParent;
37 di.LogParent = logParent;
38 di.SecureIndex = secureIndex;
39 di.Name = fs2us(fi.Name);
40 #if defined(_WIN32) && !defined(UNDER_CE)
41 // di.ShortName = fs2us(fi.ShortName);
42 #endif
43 Items.Add(di);
44
45 if (fi.IsDir())
46 Stat.NumDirs++;
47 else if (fi.IsAltStream)
48 {
49 Stat.NumAltStreams++;
50 Stat.AltStreamsSize += fi.Size;
51 }
52 else
53 {
54 Stat.NumFiles++;
55 Stat.FilesSize += fi.Size;
56 }
57}
58
59// (DWORD)E_FAIL
60#define DI_DEFAULT_ERROR ERROR_INVALID_FUNCTION
61
62HRESULT CDirItems::AddError(const FString &path, DWORD errorCode)
63{
64 if (errorCode == 0)
65 errorCode = DI_DEFAULT_ERROR;
66 Stat.NumErrors++;
67 if (Callback)
68 return Callback->ScanError(path, errorCode);
69 return S_OK;
70}
71
72HRESULT CDirItems::AddError(const FString &path)
73{
74 return AddError(path, ::GetLastError());
75}
76
77static const unsigned kScanProgressStepMask = (1 << 12) - 1;
78
79HRESULT CDirItems::ScanProgress(const FString &dirPath)
80{
81 if (Callback)
82 return Callback->ScanProgress(Stat, dirPath, true);
83 return S_OK;
84}
85
86UString CDirItems::GetPrefixesPath(const CIntVector &parents, int index, const UString &name) const
87{
88 UString path;
89 unsigned len = name.Len();
90
91 int i;
92 for (i = index; i >= 0; i = parents[(unsigned)i])
93 len += Prefixes[(unsigned)i].Len();
94
95 wchar_t *p = path.GetBuf_SetEnd(len) + len;
96
97 p -= name.Len();
98 wmemcpy(p, (const wchar_t *)name, name.Len());
99
100 for (i = index; i >= 0; i = parents[(unsigned)i])
101 {
102 const UString &s = Prefixes[(unsigned)i];
103 p -= s.Len();
104 wmemcpy(p, (const wchar_t *)s, s.Len());
105 }
106
107 return path;
108}
109
110FString CDirItems::GetPhyPath(unsigned index) const
111{
112 const CDirItem &di = Items[index];
113 return us2fs(GetPrefixesPath(PhyParents, di.PhyParent, di.Name));
114}
115
116UString CDirItems::GetLogPath(unsigned index) const
117{
118 const CDirItem &di = Items[index];
119 return GetPrefixesPath(LogParents, di.LogParent, di.Name);
120}
121
122void CDirItems::ReserveDown()
123{
124 Prefixes.ReserveDown();
125 PhyParents.ReserveDown();
126 LogParents.ReserveDown();
127 Items.ReserveDown();
128}
129
130unsigned CDirItems::AddPrefix(int phyParent, int logParent, const UString &prefix)
131{
132 PhyParents.Add(phyParent);
133 LogParents.Add(logParent);
134 return Prefixes.Add(prefix);
135}
136
137void CDirItems::DeleteLastPrefix()
138{
139 PhyParents.DeleteBack();
140 LogParents.DeleteBack();
141 Prefixes.DeleteBack();
142}
143
144bool InitLocalPrivileges();
145
146CDirItems::CDirItems():
147 SymLinks(false),
148 ScanAltStreams(false)
149 , ExcludeDirItems(false)
150 , ExcludeFileItems(false)
151 #ifdef _USE_SECURITY_CODE
152 , ReadSecure(false)
153 #endif
154 , Callback(NULL)
155{
156 #ifdef _USE_SECURITY_CODE
157 _saclEnabled = InitLocalPrivileges();
158 #endif
159}
160
161
162#ifdef _USE_SECURITY_CODE
163
164HRESULT CDirItems::AddSecurityItem(const FString &path, int &secureIndex)
165{
166 secureIndex = -1;
167
168 SECURITY_INFORMATION securInfo =
169 DACL_SECURITY_INFORMATION |
170 GROUP_SECURITY_INFORMATION |
171 OWNER_SECURITY_INFORMATION;
172 if (_saclEnabled)
173 securInfo |= SACL_SECURITY_INFORMATION;
174
175 DWORD errorCode = 0;
176 DWORD secureSize;
177
178 BOOL res = ::GetFileSecurityW(fs2us(path), securInfo, (PSECURITY_DESCRIPTOR)(void *)(Byte *)TempSecureBuf, (DWORD)TempSecureBuf.Size(), &secureSize);
179
180 if (res)
181 {
182 if (secureSize == 0)
183 return S_OK;
184 if (secureSize > TempSecureBuf.Size())
185 errorCode = ERROR_INVALID_FUNCTION;
186 }
187 else
188 {
189 errorCode = GetLastError();
190 if (errorCode == ERROR_INSUFFICIENT_BUFFER)
191 {
192 if (secureSize <= TempSecureBuf.Size())
193 errorCode = ERROR_INVALID_FUNCTION;
194 else
195 {
196 TempSecureBuf.Alloc(secureSize);
197 res = ::GetFileSecurityW(fs2us(path), securInfo, (PSECURITY_DESCRIPTOR)(void *)(Byte *)TempSecureBuf, (DWORD)TempSecureBuf.Size(), &secureSize);
198 if (res)
199 {
200 if (secureSize != TempSecureBuf.Size())
201 errorCode = ERROR_INVALID_FUNCTION;;
202 }
203 else
204 errorCode = GetLastError();
205 }
206 }
207 }
208
209 if (res)
210 {
211 secureIndex = (int)SecureBlocks.AddUniq(TempSecureBuf, secureSize);
212 return S_OK;
213 }
214
215 return AddError(path, errorCode);
216}
217
218#endif // _USE_SECURITY_CODE
219
220
221HRESULT CDirItems::EnumerateOneDir(const FString &phyPrefix, CObjectVector<NFind::CFileInfo> &files)
222{
223 NFind::CEnumerator enumerator;
224 // printf("\n enumerator.SetDirPrefix(phyPrefix) \n");
225
226 enumerator.SetDirPrefix(phyPrefix);
227
228 #ifdef _WIN32
229
230 NFind::CFileInfo fi;
231
232 for (unsigned ttt = 0; ; ttt++)
233 {
234 bool found;
235 if (!enumerator.Next(fi, found))
236 return AddError(phyPrefix);
237 if (!found)
238 return S_OK;
239 files.Add(fi);
240 if (Callback && (ttt & kScanProgressStepMask) == kScanProgressStepMask)
241 {
242 RINOK(ScanProgress(phyPrefix));
243 }
244 }
245
246 #else // _WIN32
247
248 // enumerator.SolveLinks = !SymLinks;
249
250 CObjectVector<NFind::CDirEntry> entries;
251
252 for (unsigned ttt = 0; ; ttt++)
253 {
254 bool found;
255 NFind::CDirEntry de;
256 if (!enumerator.Next(de, found))
257 {
258 return AddError(phyPrefix);
259 }
260 if (!found)
261 break;
262 entries.Add(de);
263 }
264
265 FOR_VECTOR(i, entries)
266 {
267 const NFind::CDirEntry &de = entries[i];
268 NFind::CFileInfo fi;
269 if (!enumerator.Fill_FileInfo(de, fi, !SymLinks))
270 // if (!fi.Find_AfterEnumerator(path))
271 {
272 const FString path = phyPrefix + de.Name;
273 {
274 RINOK(AddError(path));
275 continue;
276 }
277 }
278
279 files.Add(fi);
280
281 if (Callback && (i & kScanProgressStepMask) == kScanProgressStepMask)
282 {
283 RINOK(ScanProgress(phyPrefix));
284 }
285 }
286
287 return S_OK;
288
289 #endif // _WIN32
290}
291
292
293
294
295HRESULT CDirItems::EnumerateDir(int phyParent, int logParent, const FString &phyPrefix)
296{
297 RINOK(ScanProgress(phyPrefix));
298
299 CObjectVector<NFind::CFileInfo> files;
300 RINOK(EnumerateOneDir(phyPrefix, files));
301
302 FOR_VECTOR (i, files)
303 {
304 #ifdef _WIN32
305 const NFind::CFileInfo &fi = files[i];
306 #else
307 const NFind::CFileInfo &fi = files[i];
308 /*
309 NFind::CFileInfo fi;
310 {
311 const NFind::CDirEntry &di = files[i];
312 const FString path = phyPrefix + di.Name;
313 if (!fi.Find_AfterEnumerator(path))
314 {
315 RINOK(AddError(path));
316 continue;
317 }
318 fi.Name = di.Name;
319 }
320 */
321 #endif
322
323 if (CanIncludeItem(fi.IsDir()))
324 {
325 int secureIndex = -1;
326 #ifdef _USE_SECURITY_CODE
327 if (ReadSecure)
328 {
329 RINOK(AddSecurityItem(phyPrefix + fi.Name, secureIndex));
330 }
331 #endif
332 AddDirFileInfo(phyParent, logParent, secureIndex, fi);
333 }
334
335 if (Callback && (i & kScanProgressStepMask) == kScanProgressStepMask)
336 {
337 RINOK(ScanProgress(phyPrefix));
338 }
339
340 if (fi.IsDir())
341 {
342 const FString name2 = fi.Name + FCHAR_PATH_SEPARATOR;
343 unsigned parent = AddPrefix(phyParent, logParent, fs2us(name2));
344 RINOK(EnumerateDir((int)parent, (int)parent, phyPrefix + name2));
345 }
346 }
347 return S_OK;
348}
349
350
351/*
352EnumerateItems2()
353 const FStringVector &filePaths - are path without tail slashes.
354 All dir prefixes of filePaths will be not stores in logical paths
355fix it: we can scan AltStream also.
356*/
357
358#ifdef _WIN32
359// #define FOLLOW_LINK_PARAM
360// #define FOLLOW_LINK_PARAM2
361#define FOLLOW_LINK_PARAM , (!SymLinks)
362#define FOLLOW_LINK_PARAM2 , (!dirItems.SymLinks)
363#else
364#define FOLLOW_LINK_PARAM , (!SymLinks)
365#define FOLLOW_LINK_PARAM2 , (!dirItems.SymLinks)
366#endif
367
368HRESULT CDirItems::EnumerateItems2(
369 const FString &phyPrefix,
370 const UString &logPrefix,
371 const FStringVector &filePaths,
372 FStringVector *requestedPaths)
373{
374 const int phyParent = phyPrefix.IsEmpty() ? -1 : (int)AddPrefix(-1, -1, fs2us(phyPrefix));
375 const int logParent = logPrefix.IsEmpty() ? -1 : (int)AddPrefix(-1, -1, logPrefix);
376
377 FOR_VECTOR (i, filePaths)
378 {
379 const FString &filePath = filePaths[i];
380 NFind::CFileInfo fi;
381 const FString phyPath = phyPrefix + filePath;
382 if (!fi.Find(phyPath FOLLOW_LINK_PARAM))
383 {
384 RINOK(AddError(phyPath));
385 continue;
386 }
387 if (requestedPaths)
388 requestedPaths->Add(phyPath);
389
390 const int delimiter = filePath.ReverseFind_PathSepar();
391 FString phyPrefixCur;
392 int phyParentCur = phyParent;
393 if (delimiter >= 0)
394 {
395 phyPrefixCur.SetFrom(filePath, (unsigned)(delimiter + 1));
396 phyParentCur = (int)AddPrefix(phyParent, logParent, fs2us(phyPrefixCur));
397 }
398
399 if (CanIncludeItem(fi.IsDir()))
400 {
401 int secureIndex = -1;
402 #ifdef _USE_SECURITY_CODE
403 if (ReadSecure)
404 {
405 RINOK(AddSecurityItem(phyPath, secureIndex));
406 }
407 #endif
408 AddDirFileInfo(phyParentCur, logParent, secureIndex, fi);
409 }
410
411 if (fi.IsDir())
412 {
413 const FString name2 = fi.Name + FCHAR_PATH_SEPARATOR;
414 unsigned parent = AddPrefix(phyParentCur, logParent, fs2us(name2));
415 RINOK(EnumerateDir((int)parent, (int)parent, phyPrefix + phyPrefixCur + name2));
416 }
417 }
418
419 ReserveDown();
420 return S_OK;
421}
422
423
424
425
426static HRESULT EnumerateDirItems(
427 const NWildcard::CCensorNode &curNode,
428 const int phyParent, const int logParent,
429 const FString &phyPrefix,
430 const UStringVector &addParts, // additional parts from curNode
431 CDirItems &dirItems,
432 bool enterToSubFolders);
433
434
435/* EnumerateDirItems_Spec()
436 adds new Dir item prefix, and enumerates dir items,
437 then it can remove that Dir item prefix, if there are no items in that dir.
438*/
439
440
441/*
442 EnumerateDirItems_Spec()
443 it's similar to EnumerateDirItems, but phyPrefix doesn't include (curFolderName)
444*/
445
446static HRESULT EnumerateDirItems_Spec(
447 const NWildcard::CCensorNode &curNode,
448 const int phyParent, const int logParent, const FString &curFolderName,
449 const FString &phyPrefix, // without (curFolderName)
450 const UStringVector &addParts, // (curNode + addParts) includes (curFolderName)
451 CDirItems &dirItems,
452 bool enterToSubFolders)
453{
454 const FString name2 = curFolderName + FCHAR_PATH_SEPARATOR;
455 const unsigned parent = dirItems.AddPrefix(phyParent, logParent, fs2us(name2));
456 const unsigned numItems = dirItems.Items.Size();
457 HRESULT res = EnumerateDirItems(
458 curNode, (int)parent, (int)parent, phyPrefix + name2,
459 addParts, dirItems, enterToSubFolders);
460 if (numItems == dirItems.Items.Size())
461 dirItems.DeleteLastPrefix();
462 return res;
463}
464
465
466#ifndef UNDER_CE
467
468#ifdef _WIN32
469
470static HRESULT EnumerateAltStreams(
471 const NFind::CFileInfo &fi,
472 const NWildcard::CCensorNode &curNode,
473 const int phyParent, const int logParent,
474 const FString &phyPath, // with (fi.Name), without tail slash for folders
475 const UStringVector &addParts, // with (fi.Name), prefix parts from curNode
476 bool addAllSubStreams,
477 CDirItems &dirItems)
478{
479 // we don't use (ExcludeFileItems) rules for AltStreams
480 // if (dirItems.ExcludeFileItems) return S_OK;
481
482 NFind::CStreamEnumerator enumerator(phyPath);
483 for (;;)
484 {
485 NFind::CStreamInfo si;
486 bool found;
487 if (!enumerator.Next(si, found))
488 {
489 return dirItems.AddError(phyPath + FTEXT(":*")); // , (DWORD)E_FAIL
490 }
491 if (!found)
492 return S_OK;
493 if (si.IsMainStream())
494 continue;
495 UStringVector parts = addParts;
496 const UString reducedName = si.GetReducedName();
497 parts.Back() += reducedName;
498 if (curNode.CheckPathToRoot(false, parts, true))
499 continue;
500 if (!addAllSubStreams)
501 if (!curNode.CheckPathToRoot(true, parts, true))
502 continue;
503
504 NFind::CFileInfo fi2 = fi;
505 fi2.Name += us2fs(reducedName);
506 fi2.Size = si.Size;
507 fi2.Attrib &= ~(DWORD)(FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT);
508 fi2.IsAltStream = true;
509 dirItems.AddDirFileInfo(phyParent, logParent, -1, fi2);
510 }
511}
512
513#endif // _WIN32
514
515
516/* We get Reparse data and parse it.
517 If there is Reparse error, we free dirItem.Reparse data.
518 Do we need to work with empty reparse data?
519*/
520
521HRESULT CDirItems::SetLinkInfo(CDirItem &dirItem, const NFind::CFileInfo &fi,
522 const FString &phyPrefix)
523{
524 if (!SymLinks)
525 return S_OK;
526
527 #ifdef _WIN32
528 if (!fi.HasReparsePoint() || fi.IsAltStream)
529 #else // _WIN32
530 if (!fi.IsPosixLink())
531 #endif // _WIN32
532 return S_OK;
533
534 const FString path = phyPrefix + fi.Name;
535 CByteBuffer &buf = dirItem.ReparseData;
536 if (NIO::GetReparseData(path, buf))
537 {
538 // if (dirItem.ReparseData.Size() != 0)
539 Stat.FilesSize -= fi.Size;
540 return S_OK;
541 }
542
543 DWORD res = ::GetLastError();
544 buf.Free();
545 return AddError(path, res);
546}
547
548#endif // UNDER_CE
549
550
551
552static HRESULT EnumerateForItem(
553 const NFind::CFileInfo &fi,
554 const NWildcard::CCensorNode &curNode,
555 const int phyParent, const int logParent, const FString &phyPrefix,
556 const UStringVector &addParts, // additional parts from curNode, without (fi.Name)
557 CDirItems &dirItems,
558 bool enterToSubFolders)
559{
560 const UString name = fs2us(fi.Name);
561 UStringVector newParts = addParts;
562 newParts.Add(name);
563
564 // check the path in exclude rules
565 if (curNode.CheckPathToRoot(false, newParts, !fi.IsDir()))
566 return S_OK;
567
568 #if !defined(UNDER_CE)
569 int dirItemIndex = -1;
570 #if defined(_WIN32)
571 bool addAllSubStreams = false;
572 bool needAltStreams = true;
573 #endif // _WIN32
574 #endif // !defined(UNDER_CE)
575
576 // check the path in inlcude rules
577 if (curNode.CheckPathToRoot(true, newParts, !fi.IsDir()))
578 {
579 #if !defined(UNDER_CE)
580 // dirItemIndex = (int)dirItems.Items.Size();
581 #if defined(_WIN32)
582 // we will not check include rules for substreams.
583 addAllSubStreams = true;
584 #endif // _WIN32
585 #endif // !defined(UNDER_CE)
586
587 if (dirItems.CanIncludeItem(fi.IsDir()))
588 {
589 int secureIndex = -1;
590 #ifdef _USE_SECURITY_CODE
591 if (dirItems.ReadSecure)
592 {
593 RINOK(dirItems.AddSecurityItem(phyPrefix + fi.Name, secureIndex));
594 }
595 #endif
596 #if !defined(UNDER_CE)
597 dirItemIndex = (int)dirItems.Items.Size();
598 #endif // !defined(UNDER_CE)
599 dirItems.AddDirFileInfo(phyParent, logParent, secureIndex, fi);
600 }
601 else
602 {
603 #if defined(_WIN32) && !defined(UNDER_CE)
604 needAltStreams = false;
605 #endif
606 }
607
608 if (fi.IsDir())
609 enterToSubFolders = true;
610 }
611
612 #if !defined(UNDER_CE)
613
614 // we don't scan AltStreams for link files
615
616 if (dirItemIndex >= 0)
617 {
618 CDirItem &dirItem = dirItems.Items[(unsigned)dirItemIndex];
619 RINOK(dirItems.SetLinkInfo(dirItem, fi, phyPrefix));
620 if (dirItem.ReparseData.Size() != 0)
621 return S_OK;
622 }
623
624 #if defined(_WIN32)
625 if (needAltStreams && dirItems.ScanAltStreams)
626 {
627 RINOK(EnumerateAltStreams(fi, curNode, phyParent, logParent,
628 phyPrefix + fi.Name, // with (fi.Name)
629 newParts, // with (fi.Name)
630 addAllSubStreams,
631 dirItems));
632 }
633 #endif
634
635 #endif // !defined(UNDER_CE)
636
637
638 #ifndef _WIN32
639 if (!fi.IsPosixLink()) // posix link can follow to dir
640 #endif
641 if (!fi.IsDir())
642 return S_OK;
643
644 const NWildcard::CCensorNode *nextNode = NULL;
645
646 if (addParts.IsEmpty())
647 {
648 int index = curNode.FindSubNode(name);
649 if (index >= 0)
650 {
651 nextNode = &curNode.SubNodes[(unsigned)index];
652 newParts.Clear();
653 }
654 }
655
656 if (!nextNode)
657 {
658 if (!enterToSubFolders)
659 return S_OK;
660
661 #ifndef _WIN32
662 if (fi.IsPosixLink())
663 {
664 // here we can try to resolve posix link
665 // if the link to dir, then can we follow it
666 return S_OK; // we don't follow posix link
667 }
668 #endif
669
670 if (dirItems.SymLinks && fi.HasReparsePoint())
671 {
672 /* 20.03: in SymLinks mode: we don't enter to directory that
673 has reparse point and has no CCensorNode
674 NOTE: (curNode and parent nodes) still can have wildcard rules
675 to include some items of target directory (of reparse point),
676 but we ignore these rules here.
677 */
678 return S_OK;
679 }
680 nextNode = &curNode;
681 }
682
683 return EnumerateDirItems_Spec(
684 *nextNode, phyParent, logParent, fi.Name,
685 phyPrefix, // without (fi.Name)
686 newParts, // relative to (*nextNode). (*nextNode + newParts) includes (fi.Name)
687 dirItems,
688 enterToSubFolders);
689}
690
691
692static bool CanUseFsDirect(const NWildcard::CCensorNode &curNode)
693{
694 FOR_VECTOR (i, curNode.IncludeItems)
695 {
696 const NWildcard::CItem &item = curNode.IncludeItems[i];
697 if (item.Recursive || item.PathParts.Size() != 1)
698 return false;
699 const UString &name = item.PathParts.Front();
700 /*
701 if (name.IsEmpty())
702 return false;
703 */
704
705 /* Windows doesn't support file name with wildcard
706 But if another system supports file name with wildcard,
707 and wildcard mode is disabled, we can ignore wildcard in name
708 */
709 /*
710 #ifndef _WIN32
711 if (!item.WildcardParsing)
712 continue;
713 #endif
714 */
715 if (DoesNameContainWildcard(name))
716 return false;
717 }
718 return true;
719}
720
721
722#if defined(_WIN32) && !defined(UNDER_CE)
723
724static bool IsVirtualFsFolder(const FString &prefix, const UString &name)
725{
726 UString s = fs2us(prefix);
727 s += name;
728 s.Add_PathSepar();
729 // it returns (true) for non real FS folder path like - "\\SERVER\"
730 return IsPathSepar(s[0]) && GetRootPrefixSize(s) == 0;
731}
732
733#endif
734
735
736
737static HRESULT EnumerateDirItems(
738 const NWildcard::CCensorNode &curNode,
739 const int phyParent, const int logParent, const FString &phyPrefix,
740 const UStringVector &addParts, // prefix from curNode including
741 CDirItems &dirItems,
742 bool enterToSubFolders)
743{
744 if (!enterToSubFolders)
745 {
746 /* if there are IncludeItems censor rules that affect items in subdirs,
747 then we will enter to all subfolders */
748 if (curNode.NeedCheckSubDirs())
749 enterToSubFolders = true;
750 }
751
752 RINOK(dirItems.ScanProgress(phyPrefix));
753
754 // try direct_names case at first
755 if (addParts.IsEmpty() && !enterToSubFolders)
756 {
757 if (CanUseFsDirect(curNode))
758 {
759 // all names are direct (no wildcards)
760 // so we don't need file_system's dir enumerator
761 CRecordVector<bool> needEnterVector;
762 unsigned i;
763
764 for (i = 0; i < curNode.IncludeItems.Size(); i++)
765 {
766 const NWildcard::CItem &item = curNode.IncludeItems[i];
767 const UString &name = item.PathParts.Front();
768 FString fullPath = phyPrefix + us2fs(name);
769
770 /*
771 // not possible now
772 if (!item.ForDir && !item.ForFile)
773 {
774 RINOK(dirItems.AddError(fullPath, ERROR_INVALID_PARAMETER));
775 continue;
776 }
777 */
778
779 #if defined(_WIN32) && !defined(UNDER_CE)
780 bool needAltStreams = true;
781 #endif
782
783 #ifdef _USE_SECURITY_CODE
784 bool needSecurity = true;
785 #endif
786
787 if (phyPrefix.IsEmpty())
788 {
789 if (!item.ForFile)
790 {
791 /* we don't like some names for alt streams inside archive:
792 ":sname" for "\"
793 "c:::sname" for "C:\"
794 So we ignore alt streams for these cases */
795 if (name.IsEmpty())
796 {
797 #if defined(_WIN32) && !defined(UNDER_CE)
798 needAltStreams = false;
799 #endif
800
801 /*
802 // do we need to ignore security info for "\\" folder ?
803 #ifdef _USE_SECURITY_CODE
804 needSecurity = false;
805 #endif
806 */
807
808 fullPath = CHAR_PATH_SEPARATOR;
809 }
810 #if defined(_WIN32) && !defined(UNDER_CE)
811 else if (item.IsDriveItem())
812 {
813 needAltStreams = false;
814 fullPath.Add_PathSepar();
815 }
816 #endif
817 }
818 }
819
820 NFind::CFileInfo fi;
821 #if defined(_WIN32) && !defined(UNDER_CE)
822 if (IsVirtualFsFolder(phyPrefix, name))
823 {
824 fi.SetAsDir();
825 fi.Name = us2fs(name);
826 }
827 else
828 #endif
829 if (!fi.Find(fullPath FOLLOW_LINK_PARAM2))
830 {
831 RINOK(dirItems.AddError(fullPath));
832 continue;
833 }
834
835 /*
836 #ifdef _WIN32
837 #define MY_ERROR_IS_DIR ERROR_FILE_NOT_FOUND
838 #define MY_ERROR_NOT_DIR DI_DEFAULT_ERROR
839 #else
840 #define MY_ERROR_IS_DIR EISDIR
841 #define MY_ERROR_NOT_DIR ENOTDIR
842 #endif
843 */
844
845 const bool isDir = fi.IsDir();
846 if (isDir ? !item.ForDir : !item.ForFile)
847 {
848 // RINOK(dirItems.AddError(fullPath, isDir ? MY_ERROR_IS_DIR: MY_ERROR_NOT_DIR));
849 RINOK(dirItems.AddError(fullPath, DI_DEFAULT_ERROR));
850 continue;
851 }
852 {
853 UStringVector pathParts;
854 pathParts.Add(fs2us(fi.Name));
855 if (curNode.CheckPathToRoot(false, pathParts, !isDir))
856 continue;
857 }
858
859
860 if (dirItems.CanIncludeItem(fi.IsDir()))
861 {
862 int secureIndex = -1;
863 #ifdef _USE_SECURITY_CODE
864 if (needSecurity && dirItems.ReadSecure)
865 {
866 RINOK(dirItems.AddSecurityItem(fullPath, secureIndex));
867 }
868 #endif
869
870 dirItems.AddDirFileInfo(phyParent, logParent, secureIndex, fi);
871
872 // we don't scan AltStreams for link files
873
874 #if !defined(UNDER_CE)
875 {
876 CDirItem &dirItem = dirItems.Items.Back();
877 RINOK(dirItems.SetLinkInfo(dirItem, fi, phyPrefix));
878 if (dirItem.ReparseData.Size() != 0)
879 continue;
880 }
881
882 #if defined(_WIN32)
883 if (needAltStreams && dirItems.ScanAltStreams)
884 {
885 UStringVector pathParts;
886 pathParts.Add(fs2us(fi.Name));
887 RINOK(EnumerateAltStreams(fi, curNode, phyParent, logParent,
888 fullPath, // including (name)
889 pathParts, // including (fi.Name)
890 true, /* addAllSubStreams */
891 dirItems));
892 }
893 #endif // defined(_WIN32)
894
895 #endif // !defined(UNDER_CE)
896 }
897
898
899 #ifndef _WIN32
900 if (!fi.IsPosixLink()) // posix link can follow to dir
901 #endif
902 if (!isDir)
903 continue;
904
905 UStringVector newParts;
906 const NWildcard::CCensorNode *nextNode = NULL;
907 int index = curNode.FindSubNode(name);
908 if (index >= 0)
909 {
910 for (int t = (int)needEnterVector.Size(); t <= index; t++)
911 needEnterVector.Add(true);
912 needEnterVector[(unsigned)index] = false;
913 nextNode = &curNode.SubNodes[(unsigned)index];
914 }
915 else
916 {
917 #ifndef _WIN32
918 if (fi.IsPosixLink())
919 {
920 // here we can try to resolve posix link
921 // if the link to dir, then can we follow it
922 continue; // we don't follow posix link
923 }
924 #endif
925
926 if (dirItems.SymLinks)
927 {
928 if (fi.HasReparsePoint())
929 {
930 /* 20.03: in SymLinks mode: we don't enter to directory that
931 has reparse point and has no CCensorNode */
932 continue;
933 }
934 }
935 nextNode = &curNode;
936 newParts.Add(name); // don't change it to fi.Name. It's for shortnames support
937 }
938
939 RINOK(EnumerateDirItems_Spec(*nextNode, phyParent, logParent, fi.Name, phyPrefix,
940 newParts, dirItems, true));
941 }
942
943 for (i = 0; i < curNode.SubNodes.Size(); i++)
944 {
945 if (i < needEnterVector.Size())
946 if (!needEnterVector[i])
947 continue;
948 const NWildcard::CCensorNode &nextNode = curNode.SubNodes[i];
949 FString fullPath = phyPrefix + us2fs(nextNode.Name);
950 NFind::CFileInfo fi;
951
952 if (phyPrefix.IsEmpty())
953 {
954 {
955 if (nextNode.Name.IsEmpty())
956 fullPath = CHAR_PATH_SEPARATOR;
957 #ifdef _WIN32
958 else if (NWildcard::IsDriveColonName(nextNode.Name))
959 fullPath.Add_PathSepar();
960 #endif
961 }
962 }
963
964 // we don't want to call fi.Find() for root folder or virtual folder
965 if ((phyPrefix.IsEmpty() && nextNode.Name.IsEmpty())
966 #if defined(_WIN32) && !defined(UNDER_CE)
967 || IsVirtualFsFolder(phyPrefix, nextNode.Name)
968 #endif
969 )
970 {
971 fi.SetAsDir();
972 fi.Name = us2fs(nextNode.Name);
973 }
974 else
975 {
976 if (!fi.Find(fullPath FOLLOW_LINK_PARAM2))
977 {
978 if (!nextNode.AreThereIncludeItems())
979 continue;
980 RINOK(dirItems.AddError(fullPath));
981 continue;
982 }
983
984 if (!fi.IsDir())
985 {
986 RINOK(dirItems.AddError(fullPath, DI_DEFAULT_ERROR));
987 continue;
988 }
989 }
990
991 RINOK(EnumerateDirItems_Spec(nextNode, phyParent, logParent, fi.Name, phyPrefix,
992 UStringVector(), dirItems, false));
993 }
994
995 return S_OK;
996 }
997 }
998
999 #ifdef _WIN32
1000 #ifndef UNDER_CE
1001
1002 // scan drives, if wildcard is "*:\"
1003
1004 if (phyPrefix.IsEmpty() && curNode.IncludeItems.Size() > 0)
1005 {
1006 unsigned i;
1007 for (i = 0; i < curNode.IncludeItems.Size(); i++)
1008 {
1009 const NWildcard::CItem &item = curNode.IncludeItems[i];
1010 if (item.PathParts.Size() < 1)
1011 break;
1012 const UString &name = item.PathParts.Front();
1013 if (name.Len() != 2 || name[1] != ':')
1014 break;
1015 if (item.PathParts.Size() == 1)
1016 if (item.ForFile || !item.ForDir)
1017 break;
1018 if (NWildcard::IsDriveColonName(name))
1019 continue;
1020 if (name[0] != '*' && name[0] != '?')
1021 break;
1022 }
1023 if (i == curNode.IncludeItems.Size())
1024 {
1025 FStringVector driveStrings;
1026 NFind::MyGetLogicalDriveStrings(driveStrings);
1027 for (i = 0; i < driveStrings.Size(); i++)
1028 {
1029 FString driveName = driveStrings[i];
1030 if (driveName.Len() < 3 || driveName.Back() != '\\')
1031 return E_FAIL;
1032 driveName.DeleteBack();
1033 NFind::CFileInfo fi;
1034 fi.SetAsDir();
1035 fi.Name = driveName;
1036
1037 RINOK(EnumerateForItem(fi, curNode, phyParent, logParent, phyPrefix,
1038 addParts, dirItems, enterToSubFolders));
1039 }
1040 return S_OK;
1041 }
1042 }
1043
1044 #endif
1045 #endif
1046
1047
1048 CObjectVector<NFind::CFileInfo> files;
1049
1050 // for (int y = 0; y < 1; y++)
1051 {
1052 // files.Clear();
1053 RINOK(dirItems.EnumerateOneDir(phyPrefix, files));
1054 /*
1055 FOR_VECTOR (i, files)
1056 {
1057 #ifdef _WIN32
1058 // const NFind::CFileInfo &fi = files[i];
1059 #else
1060 NFind::CFileInfo &fi = files[i];
1061 {
1062 const NFind::CFileInfo &di = files[i];
1063 const FString path = phyPrefix + di.Name;
1064 if (!fi.Find_AfterEnumerator(path))
1065 {
1066 RINOK(dirItems.AddError(path));
1067 continue;
1068 }
1069 fi.Name = di.Name;
1070 }
1071 #endif
1072
1073 }
1074 */
1075 }
1076
1077 FOR_VECTOR (i, files)
1078 {
1079 #ifdef _WIN32
1080 const NFind::CFileInfo &fi = files[i];
1081 #else
1082 const NFind::CFileInfo &fi = files[i];
1083 /*
1084 NFind::CFileInfo fi;
1085 {
1086 const NFind::CDirEntry &di = files[i];
1087 const FString path = phyPrefix + di.Name;
1088 if (!fi.Find_AfterEnumerator(path))
1089 {
1090 RINOK(dirItems.AddError(path));
1091 continue;
1092 }
1093 fi.Name = di.Name;
1094 }
1095 */
1096 #endif
1097
1098 RINOK(EnumerateForItem(fi, curNode, phyParent, logParent, phyPrefix,
1099 addParts, dirItems, enterToSubFolders));
1100 if (dirItems.Callback && (i & kScanProgressStepMask) == kScanProgressStepMask)
1101 {
1102 RINOK(dirItems.ScanProgress(phyPrefix));
1103 }
1104 }
1105
1106 return S_OK;
1107}
1108
1109
1110
1111
1112HRESULT EnumerateItems(
1113 const NWildcard::CCensor &censor,
1114 const NWildcard::ECensorPathMode pathMode,
1115 const UString &addPathPrefix, // prefix that will be added to Logical Path
1116 CDirItems &dirItems)
1117{
1118 FOR_VECTOR (i, censor.Pairs)
1119 {
1120 const NWildcard::CPair &pair = censor.Pairs[i];
1121 const int phyParent = pair.Prefix.IsEmpty() ? -1 : (int)dirItems.AddPrefix(-1, -1, pair.Prefix);
1122 int logParent = -1;
1123
1124 if (pathMode == NWildcard::k_AbsPath)
1125 logParent = phyParent;
1126 else
1127 {
1128 if (!addPathPrefix.IsEmpty())
1129 logParent = (int)dirItems.AddPrefix(-1, -1, addPathPrefix);
1130 }
1131
1132 RINOK(EnumerateDirItems(pair.Head, phyParent, logParent, us2fs(pair.Prefix), UStringVector(),
1133 dirItems,
1134 false // enterToSubFolders
1135 ));
1136 }
1137 dirItems.ReserveDown();
1138
1139 #if defined(_WIN32) && !defined(UNDER_CE)
1140 RINOK(dirItems.FillFixedReparse());
1141 #endif
1142
1143 return S_OK;
1144}
1145
1146
1147
1148#if defined(_WIN32) && !defined(UNDER_CE)
1149
1150HRESULT CDirItems::FillFixedReparse()
1151{
1152 FOR_VECTOR(i, Items)
1153 {
1154 CDirItem &item = Items[i];
1155
1156 if (!SymLinks)
1157 {
1158 // continue; // for debug
1159 if (!item.Has_Attrib_ReparsePoint())
1160 continue;
1161
1162 // if (item.IsDir()) continue;
1163
1164 const FString phyPath = GetPhyPath(i);
1165
1166 NFind::CFileInfo fi;
1167 if (fi.Fill_From_ByHandleFileInfo(phyPath)) // item.IsDir()
1168 {
1169 item.Size = fi.Size;
1170 item.CTime = fi.CTime;
1171 item.ATime = fi.ATime;
1172 item.MTime = fi.MTime;
1173 item.Attrib = fi.Attrib;
1174 continue;
1175 }
1176
1177 /*
1178 // we request properties of target file instead of properies of symbolic link
1179 // here we also can manually parse unsupported links (like WSL links)
1180 NIO::CInFile inFile;
1181 if (inFile.Open(phyPath))
1182 {
1183 BY_HANDLE_FILE_INFORMATION info;
1184 if (inFile.GetFileInformation(&info))
1185 {
1186 // Stat.FilesSize doesn't contain item.Size already
1187 // Stat.FilesSize -= item.Size;
1188 item.Size = (((UInt64)info.nFileSizeHigh) << 32) + info.nFileSizeLow;
1189 Stat.FilesSize += item.Size;
1190 item.CTime = info.ftCreationTime;
1191 item.ATime = info.ftLastAccessTime;
1192 item.MTime = info.ftLastWriteTime;
1193 item.Attrib = info.dwFileAttributes;
1194 continue;
1195 }
1196 }
1197 */
1198
1199 RINOK(AddError(phyPath));
1200 continue;
1201 }
1202
1203 // (SymLinks == true) here
1204
1205 if (item.ReparseData.Size() == 0)
1206 continue;
1207
1208 // if (item.Size == 0)
1209 {
1210 // 20.03: we use Reparse Data instead of real data
1211 item.Size = item.ReparseData.Size();
1212 }
1213
1214 CReparseAttr attr;
1215 if (!attr.Parse(item.ReparseData, item.ReparseData.Size()))
1216 {
1217 const FString phyPath = GetPhyPath(i);
1218 AddError(phyPath, attr.ErrorCode);
1219 continue;
1220 }
1221
1222 /* imagex/WIM reduces absolute paths in links (raparse data),
1223 if we archive non root folder. We do same thing here */
1224
1225 bool isWSL = false;
1226 if (attr.IsSymLink_WSL())
1227 {
1228 // isWSL = true;
1229 // we don't change WSL symlinks
1230 continue;
1231 }
1232 else
1233 {
1234 if (attr.IsRelative_Win())
1235 continue;
1236 }
1237
1238 const UString &link = attr.GetPath();
1239 if (!IsDrivePath(link))
1240 continue;
1241 // maybe we need to support networks paths also ?
1242
1243 FString fullPathF;
1244 if (!NDir::MyGetFullPathName(GetPhyPath(i), fullPathF))
1245 continue;
1246 const UString fullPath = fs2us(fullPathF);
1247 const UString logPath = GetLogPath(i);
1248 if (logPath.Len() >= fullPath.Len())
1249 continue;
1250 if (CompareFileNames(logPath, fullPath.RightPtr(logPath.Len())) != 0)
1251 continue;
1252
1253 const UString prefix = fullPath.Left(fullPath.Len() - logPath.Len());
1254 if (!IsPathSepar(prefix.Back()))
1255 continue;
1256
1257 const unsigned rootPrefixSize = GetRootPrefixSize(prefix);
1258 if (rootPrefixSize == 0)
1259 continue;
1260 if (rootPrefixSize == prefix.Len())
1261 continue; // simple case: paths are from root
1262
1263 if (link.Len() <= prefix.Len())
1264 continue;
1265
1266 if (CompareFileNames(link.Left(prefix.Len()), prefix) != 0)
1267 continue;
1268
1269 UString newLink = prefix.Left(rootPrefixSize);
1270 newLink += link.Ptr(prefix.Len());
1271
1272 CByteBuffer data;
1273 bool isSymLink = !attr.IsMountPoint();
1274 if (!FillLinkData(data, newLink, isSymLink, isWSL))
1275 continue;
1276 item.ReparseData2 = data;
1277 }
1278 return S_OK;
1279}
1280
1281#endif
1282
1283
1284
1285static const char * const kCannotFindArchive = "Cannot find archive";
1286
1287HRESULT EnumerateDirItemsAndSort(
1288 NWildcard::CCensor &censor,
1289 NWildcard::ECensorPathMode censorPathMode,
1290 const UString &addPathPrefix,
1291 UStringVector &sortedPaths,
1292 UStringVector &sortedFullPaths,
1293 CDirItemsStat &st,
1294 IDirItemsCallback *callback)
1295{
1296 FStringVector paths;
1297
1298 {
1299 CDirItems dirItems;
1300 dirItems.Callback = callback;
1301 {
1302 HRESULT res = EnumerateItems(censor, censorPathMode, addPathPrefix, dirItems);
1303 st = dirItems.Stat;
1304 RINOK(res);
1305 }
1306
1307 FOR_VECTOR (i, dirItems.Items)
1308 {
1309 const CDirItem &dirItem = dirItems.Items[i];
1310 if (!dirItem.IsDir())
1311 paths.Add(dirItems.GetPhyPath(i));
1312 }
1313 }
1314
1315 if (paths.Size() == 0)
1316 {
1317 // return S_OK;
1318 throw CMessagePathException(kCannotFindArchive);
1319 }
1320
1321 UStringVector fullPaths;
1322
1323 unsigned i;
1324
1325 for (i = 0; i < paths.Size(); i++)
1326 {
1327 FString fullPath;
1328 NFile::NDir::MyGetFullPathName(paths[i], fullPath);
1329 fullPaths.Add(fs2us(fullPath));
1330 }
1331
1332 CUIntVector indices;
1333 SortFileNames(fullPaths, indices);
1334 sortedPaths.ClearAndReserve(indices.Size());
1335 sortedFullPaths.ClearAndReserve(indices.Size());
1336
1337 for (i = 0; i < indices.Size(); i++)
1338 {
1339 unsigned index = indices[i];
1340 sortedPaths.AddInReserved(fs2us(paths[index]));
1341 sortedFullPaths.AddInReserved(fullPaths[index]);
1342 if (i > 0 && CompareFileNames(sortedFullPaths[i], sortedFullPaths[i - 1]) == 0)
1343 throw CMessagePathException("Duplicate archive path:", sortedFullPaths[i]);
1344 }
1345
1346 return S_OK;
1347}
1348
1349
1350
1351
1352#ifdef _WIN32
1353
1354// This code converts all short file names to long file names.
1355
1356static void ConvertToLongName(const UString &prefix, UString &name)
1357{
1358 if (name.IsEmpty() || DoesNameContainWildcard(name))
1359 return;
1360 NFind::CFileInfo fi;
1361 const FString path (us2fs(prefix + name));
1362 #ifndef UNDER_CE
1363 if (NFile::NName::IsDevicePath(path))
1364 return;
1365 #endif
1366 if (fi.Find(path))
1367 name = fs2us(fi.Name);
1368}
1369
1370static void ConvertToLongNames(const UString &prefix, CObjectVector<NWildcard::CItem> &items)
1371{
1372 FOR_VECTOR (i, items)
1373 {
1374 NWildcard::CItem &item = items[i];
1375 if (item.Recursive || item.PathParts.Size() != 1)
1376 continue;
1377 if (prefix.IsEmpty() && item.IsDriveItem())
1378 continue;
1379 ConvertToLongName(prefix, item.PathParts.Front());
1380 }
1381}
1382
1383static void ConvertToLongNames(const UString &prefix, NWildcard::CCensorNode &node)
1384{
1385 ConvertToLongNames(prefix, node.IncludeItems);
1386 ConvertToLongNames(prefix, node.ExcludeItems);
1387 unsigned i;
1388 for (i = 0; i < node.SubNodes.Size(); i++)
1389 {
1390 UString &name = node.SubNodes[i].Name;
1391 if (prefix.IsEmpty() && NWildcard::IsDriveColonName(name))
1392 continue;
1393 ConvertToLongName(prefix, name);
1394 }
1395 // mix folders with same name
1396 for (i = 0; i < node.SubNodes.Size(); i++)
1397 {
1398 NWildcard::CCensorNode &nextNode1 = node.SubNodes[i];
1399 for (unsigned j = i + 1; j < node.SubNodes.Size();)
1400 {
1401 const NWildcard::CCensorNode &nextNode2 = node.SubNodes[j];
1402 if (nextNode1.Name.IsEqualTo_NoCase(nextNode2.Name))
1403 {
1404 nextNode1.IncludeItems += nextNode2.IncludeItems;
1405 nextNode1.ExcludeItems += nextNode2.ExcludeItems;
1406 node.SubNodes.Delete(j);
1407 }
1408 else
1409 j++;
1410 }
1411 }
1412 for (i = 0; i < node.SubNodes.Size(); i++)
1413 {
1414 NWildcard::CCensorNode &nextNode = node.SubNodes[i];
1415 ConvertToLongNames(prefix + nextNode.Name + WCHAR_PATH_SEPARATOR, nextNode);
1416 }
1417}
1418
1419void ConvertToLongNames(NWildcard::CCensor &censor)
1420{
1421 FOR_VECTOR (i, censor.Pairs)
1422 {
1423 NWildcard::CPair &pair = censor.Pairs[i];
1424 ConvertToLongNames(pair.Prefix, pair.Head);
1425 }
1426}
1427
1428#endif
1429
1430
1431CMessagePathException::CMessagePathException(const char *a, const wchar_t *u)
1432{
1433 (*this) += a;
1434 if (u)
1435 {
1436 Add_LF();
1437 (*this) += u;
1438 }
1439}
1440
1441CMessagePathException::CMessagePathException(const wchar_t *a, const wchar_t *u)
1442{
1443 (*this) += a;
1444 if (u)
1445 {
1446 Add_LF();
1447 (*this) += u;
1448 }
1449}
diff --git a/CPP/7zip/UI/Common/EnumDirItems.h b/CPP/7zip/UI/Common/EnumDirItems.h
new file mode 100644
index 0000000..9b17c60
--- /dev/null
+++ b/CPP/7zip/UI/Common/EnumDirItems.h
@@ -0,0 +1,38 @@
1// EnumDirItems.h
2
3#ifndef __ENUM_DIR_ITEMS_H
4#define __ENUM_DIR_ITEMS_H
5
6#include "../../../Common/Wildcard.h"
7
8#include "DirItem.h"
9
10
11HRESULT EnumerateItems(
12 const NWildcard::CCensor &censor,
13 NWildcard::ECensorPathMode pathMode,
14 const UString &addPathPrefix,
15 CDirItems &dirItems);
16
17
18struct CMessagePathException: public UString
19{
20 CMessagePathException(const char *a, const wchar_t *u = NULL);
21 CMessagePathException(const wchar_t *a, const wchar_t *u = NULL);
22};
23
24
25HRESULT EnumerateDirItemsAndSort(
26 NWildcard::CCensor &censor,
27 NWildcard::ECensorPathMode pathMode,
28 const UString &addPathPrefix,
29 UStringVector &sortedPaths,
30 UStringVector &sortedFullPaths,
31 CDirItemsStat &st,
32 IDirItemsCallback *callback);
33
34#ifdef _WIN32
35void ConvertToLongNames(NWildcard::CCensor &censor);
36#endif
37
38#endif
diff --git a/CPP/7zip/UI/Common/ExitCode.h b/CPP/7zip/UI/Common/ExitCode.h
new file mode 100644
index 0000000..b6d7d4d
--- /dev/null
+++ b/CPP/7zip/UI/Common/ExitCode.h
@@ -0,0 +1,27 @@
1// ExitCode.h
2
3#ifndef __EXIT_CODE_H
4#define __EXIT_CODE_H
5
6namespace NExitCode {
7
8enum EEnum {
9
10 kSuccess = 0, // Successful operation
11 kWarning = 1, // Non fatal error(s) occurred
12 kFatalError = 2, // A fatal error occurred
13 // kCRCError = 3, // A CRC error occurred when unpacking
14 // kLockedArchive = 4, // Attempt to modify an archive previously locked
15 // kWriteError = 5, // Write to disk error
16 // kOpenError = 6, // Open file error
17 kUserError = 7, // Command line option error
18 kMemoryError = 8, // Not enough memory for operation
19 // kCreateFileError = 9, // Create file error
20
21 kUserBreak = 255 // User stopped the process
22
23};
24
25}
26
27#endif
diff --git a/CPP/7zip/UI/Common/Extract.cpp b/CPP/7zip/UI/Common/Extract.cpp
new file mode 100644
index 0000000..de2aeb2
--- /dev/null
+++ b/CPP/7zip/UI/Common/Extract.cpp
@@ -0,0 +1,536 @@
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/FileName.h"
11#include "../../../Windows/ErrorMsg.h"
12#include "../../../Windows/PropVariant.h"
13#include "../../../Windows/PropVariantConv.h"
14
15#include "../Common/ExtractingFilePath.h"
16#include "../Common/HashCalc.h"
17
18#include "Extract.h"
19#include "SetProperties.h"
20
21using namespace NWindows;
22using namespace NFile;
23using namespace NDir;
24
25
26static void SetErrorMessage(const char *message,
27 const FString &path, HRESULT errorCode,
28 UString &s)
29{
30 s = message;
31 s += " : ";
32 s += NError::MyFormatMessage(errorCode);
33 s += " : ";
34 s += fs2us(path);
35}
36
37
38static HRESULT DecompressArchive(
39 CCodecs *codecs,
40 const CArchiveLink &arcLink,
41 UInt64 packSize,
42 const NWildcard::CCensorNode &wildcardCensor,
43 const CExtractOptions &options,
44 bool calcCrc,
45 IExtractCallbackUI *callback,
46 CArchiveExtractCallback *ecs,
47 UString &errorMessage,
48 UInt64 &stdInProcessed)
49{
50 const CArc &arc = arcLink.Arcs.Back();
51 stdInProcessed = 0;
52 IInArchive *archive = arc.Archive;
53 CRecordVector<UInt32> realIndices;
54
55 UStringVector removePathParts;
56
57 FString outDir = options.OutputDir;
58 UString replaceName = arc.DefaultName;
59
60 if (arcLink.Arcs.Size() > 1)
61 {
62 // Most "pe" archives have same name of archive subfile "[0]" or ".rsrc_1".
63 // So it extracts different archives to one folder.
64 // We will use top level archive name
65 const CArc &arc0 = arcLink.Arcs[0];
66 if (arc0.FormatIndex >= 0 && StringsAreEqualNoCase_Ascii(codecs->Formats[(unsigned)arc0.FormatIndex].Name, "pe"))
67 replaceName = arc0.DefaultName;
68 }
69
70 outDir.Replace(FString("*"), us2fs(Get_Correct_FsFile_Name(replaceName)));
71
72 bool elimIsPossible = false;
73 UString elimPrefix; // only pure name without dir delimiter
74 FString outDirReduced = outDir;
75
76 if (options.ElimDup.Val && options.PathMode != NExtract::NPathMode::kAbsPaths)
77 {
78 UString dirPrefix;
79 SplitPathToParts_Smart(fs2us(outDir), dirPrefix, elimPrefix);
80 if (!elimPrefix.IsEmpty())
81 {
82 if (IsPathSepar(elimPrefix.Back()))
83 elimPrefix.DeleteBack();
84 if (!elimPrefix.IsEmpty())
85 {
86 outDirReduced = us2fs(dirPrefix);
87 elimIsPossible = true;
88 }
89 }
90 }
91
92 const bool allFilesAreAllowed = wildcardCensor.AreAllAllowed();
93
94 if (!options.StdInMode)
95 {
96 UInt32 numItems;
97 RINOK(archive->GetNumberOfItems(&numItems));
98
99 CReadArcItem item;
100
101 for (UInt32 i = 0; i < numItems; i++)
102 {
103 if (elimIsPossible
104 || !allFilesAreAllowed
105 || options.ExcludeDirItems
106 || options.ExcludeFileItems)
107 {
108 RINOK(arc.GetItem(i, item));
109 if (item.IsDir ? options.ExcludeDirItems : options.ExcludeFileItems)
110 continue;
111 }
112 else
113 {
114 #ifdef SUPPORT_ALT_STREAMS
115 item.IsAltStream = false;
116 if (!options.NtOptions.AltStreams.Val && arc.Ask_AltStream)
117 {
118 RINOK(Archive_IsItem_AltStream(arc.Archive, i, item.IsAltStream));
119 }
120 #endif
121 }
122
123 #ifdef SUPPORT_ALT_STREAMS
124 if (!options.NtOptions.AltStreams.Val && item.IsAltStream)
125 continue;
126 #endif
127
128 if (elimIsPossible)
129 {
130 const UString &s =
131 #ifdef SUPPORT_ALT_STREAMS
132 item.MainPath;
133 #else
134 item.Path;
135 #endif
136 if (!IsPath1PrefixedByPath2(s, elimPrefix))
137 elimIsPossible = false;
138 else
139 {
140 wchar_t c = s[elimPrefix.Len()];
141 if (c == 0)
142 {
143 if (!item.MainIsDir)
144 elimIsPossible = false;
145 }
146 else if (!IsPathSepar(c))
147 elimIsPossible = false;
148 }
149 }
150
151 if (!allFilesAreAllowed)
152 {
153 if (!CensorNode_CheckPath(wildcardCensor, item))
154 continue;
155 }
156
157 realIndices.Add(i);
158 }
159
160 if (realIndices.Size() == 0)
161 {
162 callback->ThereAreNoFiles();
163 return callback->ExtractResult(S_OK);
164 }
165 }
166
167 if (elimIsPossible)
168 {
169 removePathParts.Add(elimPrefix);
170 // outDir = outDirReduced;
171 }
172
173 #ifdef _WIN32
174 // GetCorrectFullFsPath doesn't like "..".
175 // outDir.TrimRight();
176 // outDir = GetCorrectFullFsPath(outDir);
177 #endif
178
179 if (outDir.IsEmpty())
180 outDir = "." STRING_PATH_SEPARATOR;
181 /*
182 #ifdef _WIN32
183 else if (NName::IsAltPathPrefix(outDir)) {}
184 #endif
185 */
186 else if (!CreateComplexDir(outDir))
187 {
188 const HRESULT res = GetLastError_noZero_HRESULT();
189 SetErrorMessage("Cannot create output directory", outDir, res, errorMessage);
190 return res;
191 }
192
193 ecs->Init(
194 options.NtOptions,
195 options.StdInMode ? &wildcardCensor : NULL,
196 &arc,
197 callback,
198 options.StdOutMode, options.TestMode,
199 outDir,
200 removePathParts, false,
201 packSize);
202
203
204 #ifdef SUPPORT_LINKS
205
206 if (!options.StdInMode &&
207 !options.TestMode &&
208 options.NtOptions.HardLinks.Val)
209 {
210 RINOK(ecs->PrepareHardLinks(&realIndices));
211 }
212
213 #endif
214
215
216 HRESULT result;
217 Int32 testMode = (options.TestMode && !calcCrc) ? 1: 0;
218
219 CArchiveExtractCallback_Closer ecsCloser(ecs);
220
221 if (options.StdInMode)
222 {
223 result = archive->Extract(NULL, (UInt32)(Int32)-1, testMode, ecs);
224 NCOM::CPropVariant prop;
225 if (archive->GetArchiveProperty(kpidPhySize, &prop) == S_OK)
226 ConvertPropVariantToUInt64(prop, stdInProcessed);
227 }
228 else
229 result = archive->Extract(&realIndices.Front(), realIndices.Size(), testMode, ecs);
230
231 HRESULT res2 = ecsCloser.Close();
232 if (result == S_OK)
233 result = res2;
234
235 return callback->ExtractResult(result);
236}
237
238/* v9.31: BUG was fixed:
239 Sorted list for file paths was sorted with case insensitive compare function.
240 But FindInSorted function did binary search via case sensitive compare function */
241
242int Find_FileName_InSortedVector(const UStringVector &fileName, const UString &name);
243int Find_FileName_InSortedVector(const UStringVector &fileName, const UString &name)
244{
245 unsigned left = 0, right = fileName.Size();
246 while (left != right)
247 {
248 unsigned mid = (left + right) / 2;
249 const UString &midValue = fileName[mid];
250 int compare = CompareFileNames(name, midValue);
251 if (compare == 0)
252 return (int)mid;
253 if (compare < 0)
254 right = mid;
255 else
256 left = mid + 1;
257 }
258 return -1;
259}
260
261
262
263HRESULT Extract(
264 // DECL_EXTERNAL_CODECS_LOC_VARS
265 CCodecs *codecs,
266 const CObjectVector<COpenType> &types,
267 const CIntVector &excludedFormats,
268 UStringVector &arcPaths, UStringVector &arcPathsFull,
269 const NWildcard::CCensorNode &wildcardCensor,
270 const CExtractOptions &options,
271 IOpenCallbackUI *openCallback,
272 IExtractCallbackUI *extractCallback,
273 #ifndef _SFX
274 IHashCalc *hash,
275 #endif
276 UString &errorMessage,
277 CDecompressStat &st)
278{
279 st.Clear();
280 UInt64 totalPackSize = 0;
281 CRecordVector<UInt64> arcSizes;
282
283 unsigned numArcs = options.StdInMode ? 1 : arcPaths.Size();
284
285 unsigned i;
286
287 for (i = 0; i < numArcs; i++)
288 {
289 NFind::CFileInfo fi;
290 fi.Size = 0;
291 if (!options.StdInMode)
292 {
293 const FString arcPath = us2fs(arcPaths[i]);
294 if (!fi.Find_FollowLink(arcPath))
295 {
296 const HRESULT errorCode = GetLastError_noZero_HRESULT();
297 SetErrorMessage("Cannot find archive file", arcPath, errorCode, errorMessage);
298 return errorCode;
299 }
300 if (fi.IsDir())
301 {
302 HRESULT errorCode = E_FAIL;
303 SetErrorMessage("The item is a directory", arcPath, errorCode, errorMessage);
304 return errorCode;
305 }
306 }
307 arcSizes.Add(fi.Size);
308 totalPackSize += fi.Size;
309 }
310
311 CBoolArr skipArcs(numArcs);
312 for (i = 0; i < numArcs; i++)
313 skipArcs[i] = false;
314
315 CArchiveExtractCallback *ecs = new CArchiveExtractCallback;
316 CMyComPtr<IArchiveExtractCallback> ec(ecs);
317 bool multi = (numArcs > 1);
318 ecs->InitForMulti(multi, options.PathMode, options.OverwriteMode,
319 false // keepEmptyDirParts
320 );
321 #ifndef _SFX
322 ecs->SetHashMethods(hash);
323 #endif
324
325 if (multi)
326 {
327 RINOK(extractCallback->SetTotal(totalPackSize));
328 }
329
330 UInt64 totalPackProcessed = 0;
331 bool thereAreNotOpenArcs = false;
332
333 for (i = 0; i < numArcs; i++)
334 {
335 if (skipArcs[i])
336 continue;
337
338 const UString &arcPath = arcPaths[i];
339 NFind::CFileInfo fi;
340 if (options.StdInMode)
341 {
342 fi.Size = 0;
343 fi.Attrib = 0;
344 }
345 else
346 {
347 if (!fi.Find_FollowLink(us2fs(arcPath)) || fi.IsDir())
348 {
349 const HRESULT errorCode = GetLastError_noZero_HRESULT();
350 SetErrorMessage("Cannot find archive file", us2fs(arcPath), errorCode, errorMessage);
351 return errorCode;
352 }
353 }
354
355 /*
356 #ifndef _NO_CRYPTO
357 openCallback->Open_Clear_PasswordWasAsked_Flag();
358 #endif
359 */
360
361 RINOK(extractCallback->BeforeOpen(arcPath, options.TestMode));
362 CArchiveLink arcLink;
363
364 CObjectVector<COpenType> types2 = types;
365 /*
366 #ifndef _SFX
367 if (types.IsEmpty())
368 {
369 int pos = arcPath.ReverseFind(L'.');
370 if (pos >= 0)
371 {
372 UString s = arcPath.Ptr(pos + 1);
373 int index = codecs->FindFormatForExtension(s);
374 if (index >= 0 && s == L"001")
375 {
376 s = arcPath.Left(pos);
377 pos = s.ReverseFind(L'.');
378 if (pos >= 0)
379 {
380 int index2 = codecs->FindFormatForExtension(s.Ptr(pos + 1));
381 if (index2 >= 0) // && s.CompareNoCase(L"rar") != 0
382 {
383 types2.Add(index2);
384 types2.Add(index);
385 }
386 }
387 }
388 }
389 }
390 #endif
391 */
392
393 COpenOptions op;
394 #ifndef _SFX
395 op.props = &options.Properties;
396 #endif
397 op.codecs = codecs;
398 op.types = &types2;
399 op.excludedFormats = &excludedFormats;
400 op.stdInMode = options.StdInMode;
401 op.stream = NULL;
402 op.filePath = arcPath;
403
404 HRESULT result = arcLink.Open_Strict(op, openCallback);
405
406 if (result == E_ABORT)
407 return result;
408
409 // arcLink.Set_ErrorsText();
410 RINOK(extractCallback->OpenResult(codecs, arcLink, arcPath, result));
411
412 if (result != S_OK)
413 {
414 thereAreNotOpenArcs = true;
415 if (!options.StdInMode)
416 totalPackProcessed += fi.Size;
417 continue;
418 }
419
420 if (arcLink.Arcs.Size() != 0)
421 {
422 if (arcLink.GetArc()->IsHashHandler(op))
423 {
424 if (!options.TestMode)
425 {
426 /* real Extracting to files is possible.
427 But user can think that hash archive contains real files.
428 So we block extracting here. */
429 return E_NOTIMPL;
430 }
431 FString dirPrefix = us2fs(options.HashDir);
432 if (dirPrefix.IsEmpty())
433 {
434 if (!NFile::NDir::GetOnlyDirPrefix(us2fs(arcPath), dirPrefix))
435 {
436 // return GetLastError_noZero_HRESULT();
437 }
438 }
439 if (!dirPrefix.IsEmpty())
440 NName::NormalizeDirPathPrefix(dirPrefix);
441 ecs->DirPathPrefix_for_HashFiles = dirPrefix;
442 }
443 }
444
445 if (!options.StdInMode)
446 {
447 // numVolumes += arcLink.VolumePaths.Size();
448 // arcLink.VolumesSize;
449
450 // totalPackSize -= DeleteUsedFileNamesFromList(arcLink, i + 1, arcPaths, arcPathsFull, &arcSizes);
451 // numArcs = arcPaths.Size();
452 if (arcLink.VolumePaths.Size() != 0)
453 {
454 Int64 correctionSize = (Int64)arcLink.VolumesSize;
455 FOR_VECTOR (v, arcLink.VolumePaths)
456 {
457 int index = Find_FileName_InSortedVector(arcPathsFull, arcLink.VolumePaths[v]);
458 if (index >= 0)
459 {
460 if ((unsigned)index > i)
461 {
462 skipArcs[(unsigned)index] = true;
463 correctionSize -= arcSizes[(unsigned)index];
464 }
465 }
466 }
467 if (correctionSize != 0)
468 {
469 Int64 newPackSize = (Int64)totalPackSize + correctionSize;
470 if (newPackSize < 0)
471 newPackSize = 0;
472 totalPackSize = (UInt64)newPackSize;
473 RINOK(extractCallback->SetTotal(totalPackSize));
474 }
475 }
476 }
477
478 /*
479 // Now openCallback and extractCallback use same object. So we don't need to send password.
480
481 #ifndef _NO_CRYPTO
482 bool passwordIsDefined;
483 UString password;
484 RINOK(openCallback->Open_GetPasswordIfAny(passwordIsDefined, password));
485 if (passwordIsDefined)
486 {
487 RINOK(extractCallback->SetPassword(password));
488 }
489 #endif
490 */
491
492 CArc &arc = arcLink.Arcs.Back();
493 arc.MTimeDefined = (!options.StdInMode && !fi.IsDevice);
494 arc.MTime = fi.MTime;
495
496 UInt64 packProcessed;
497 bool calcCrc =
498 #ifndef _SFX
499 (hash != NULL);
500 #else
501 false;
502 #endif
503
504 RINOK(DecompressArchive(
505 codecs,
506 arcLink,
507 fi.Size + arcLink.VolumesSize,
508 wildcardCensor,
509 options,
510 calcCrc,
511 extractCallback, ecs, errorMessage, packProcessed));
512
513 if (!options.StdInMode)
514 packProcessed = fi.Size + arcLink.VolumesSize;
515 totalPackProcessed += packProcessed;
516 ecs->LocalProgressSpec->InSize += packProcessed;
517 ecs->LocalProgressSpec->OutSize = ecs->UnpackSize;
518 if (!errorMessage.IsEmpty())
519 return E_FAIL;
520 }
521
522 if (multi || thereAreNotOpenArcs)
523 {
524 RINOK(extractCallback->SetTotal(totalPackSize));
525 RINOK(extractCallback->SetCompleted(&totalPackProcessed));
526 }
527
528 st.NumFolders = ecs->NumFolders;
529 st.NumFiles = ecs->NumFiles;
530 st.NumAltStreams = ecs->NumAltStreams;
531 st.UnpackSize = ecs->UnpackSize;
532 st.AltStreams_UnpackSize = ecs->AltStreams_UnpackSize;
533 st.NumArchives = arcPaths.Size();
534 st.PackSize = ecs->LocalProgressSpec->InSize;
535 return S_OK;
536}
diff --git a/CPP/7zip/UI/Common/Extract.h b/CPP/7zip/UI/Common/Extract.h
new file mode 100644
index 0000000..10e06da
--- /dev/null
+++ b/CPP/7zip/UI/Common/Extract.h
@@ -0,0 +1,103 @@
1// Extract.h
2
3#ifndef __EXTRACT_H
4#define __EXTRACT_H
5
6#include "../../../Windows/FileFind.h"
7
8#include "../../Archive/IArchive.h"
9
10#include "ArchiveExtractCallback.h"
11#include "ArchiveOpenCallback.h"
12#include "ExtractMode.h"
13#include "Property.h"
14
15#include "../Common/LoadCodecs.h"
16
17struct CExtractOptionsBase
18{
19 CBoolPair ElimDup;
20
21 bool ExcludeDirItems;
22 bool ExcludeFileItems;
23
24 bool PathMode_Force;
25 bool OverwriteMode_Force;
26 NExtract::NPathMode::EEnum PathMode;
27 NExtract::NOverwriteMode::EEnum OverwriteMode;
28
29 FString OutputDir;
30 CExtractNtOptions NtOptions;
31 UString HashDir;
32
33 CExtractOptionsBase():
34 ExcludeDirItems(false),
35 ExcludeFileItems(false),
36 PathMode_Force(false),
37 OverwriteMode_Force(false),
38 PathMode(NExtract::NPathMode::kFullPaths),
39 OverwriteMode(NExtract::NOverwriteMode::kAsk)
40 {}
41};
42
43struct CExtractOptions: public CExtractOptionsBase
44{
45 bool StdInMode;
46 bool StdOutMode;
47 bool YesToAll;
48 bool TestMode;
49
50 // bool ShowDialog;
51 // bool PasswordEnabled;
52 // UString Password;
53 #ifndef _SFX
54 CObjectVector<CProperty> Properties;
55 #endif
56
57 /*
58 #ifdef EXTERNAL_CODECS
59 CCodecs *Codecs;
60 #endif
61 */
62
63 CExtractOptions():
64 StdInMode(false),
65 StdOutMode(false),
66 YesToAll(false),
67 TestMode(false)
68 {}
69};
70
71struct CDecompressStat
72{
73 UInt64 NumArchives;
74 UInt64 UnpackSize;
75 UInt64 AltStreams_UnpackSize;
76 UInt64 PackSize;
77 UInt64 NumFolders;
78 UInt64 NumFiles;
79 UInt64 NumAltStreams;
80
81 void Clear()
82 {
83 NumArchives = UnpackSize = AltStreams_UnpackSize = PackSize = NumFolders = NumFiles = NumAltStreams = 0;
84 }
85};
86
87HRESULT Extract(
88 // DECL_EXTERNAL_CODECS_LOC_VARS
89 CCodecs *codecs,
90 const CObjectVector<COpenType> &types,
91 const CIntVector &excludedFormats,
92 UStringVector &archivePaths, UStringVector &archivePathsFull,
93 const NWildcard::CCensorNode &wildcardCensor,
94 const CExtractOptions &options,
95 IOpenCallbackUI *openCallback,
96 IExtractCallbackUI *extractCallback,
97 #ifndef _SFX
98 IHashCalc *hash,
99 #endif
100 UString &errorMessage,
101 CDecompressStat &st);
102
103#endif
diff --git a/CPP/7zip/UI/Common/ExtractMode.h b/CPP/7zip/UI/Common/ExtractMode.h
new file mode 100644
index 0000000..3b2b9a0
--- /dev/null
+++ b/CPP/7zip/UI/Common/ExtractMode.h
@@ -0,0 +1,34 @@
1// ExtractMode.h
2
3#ifndef __EXTRACT_MODE_H
4#define __EXTRACT_MODE_H
5
6namespace NExtract {
7
8namespace NPathMode
9{
10 enum EEnum
11 {
12 kFullPaths,
13 kCurPaths,
14 kNoPaths,
15 kAbsPaths,
16 kNoPathsAlt // alt streams must be extracted without name of base file
17 };
18}
19
20namespace NOverwriteMode
21{
22 enum EEnum
23 {
24 kAsk,
25 kOverwrite,
26 kSkip,
27 kRename,
28 kRenameExisting
29 };
30}
31
32}
33
34#endif
diff --git a/CPP/7zip/UI/Common/ExtractingFilePath.cpp b/CPP/7zip/UI/Common/ExtractingFilePath.cpp
new file mode 100644
index 0000000..21a306d
--- /dev/null
+++ b/CPP/7zip/UI/Common/ExtractingFilePath.cpp
@@ -0,0 +1,287 @@
1// ExtractingFilePath.cpp
2
3#include "StdAfx.h"
4
5#include "../../../Common/Wildcard.h"
6
7#include "../../../Windows/FileName.h"
8
9#include "ExtractingFilePath.h"
10
11extern
12bool g_PathTrailReplaceMode;
13bool g_PathTrailReplaceMode =
14 #ifdef _WIN32
15 true
16 #else
17 false
18 #endif
19 ;
20
21
22#ifdef _WIN32
23static void ReplaceIncorrectChars(UString &s)
24{
25 {
26 for (unsigned i = 0; i < s.Len(); i++)
27 {
28 wchar_t c = s[i];
29 if (
30 #ifdef _WIN32
31 c == ':' || c == '*' || c == '?' || c < 0x20 || c == '<' || c == '>' || c == '|' || c == '"'
32 || c == '/'
33 // || c == 0x202E // RLO
34 ||
35 #endif
36 c == WCHAR_PATH_SEPARATOR)
37 s.ReplaceOneCharAtPos(i,
38 '_' // default
39 // (wchar_t)(0xf000 + c) // 21.02 debug: WSL encoding for unsupported characters
40 );
41 }
42 }
43
44 if (g_PathTrailReplaceMode)
45 {
46 /*
47 // if (g_PathTrailReplaceMode == 1)
48 {
49 if (!s.IsEmpty())
50 {
51 wchar_t c = s.Back();
52 if (c == '.' || c == ' ')
53 {
54 // s += (wchar_t)(0x9c); // STRING TERMINATOR
55 s += (wchar_t)'_';
56 }
57 }
58 }
59 else
60 */
61 {
62 unsigned i;
63 for (i = s.Len(); i != 0;)
64 {
65 wchar_t c = s[i - 1];
66 if (c != '.' && c != ' ')
67 break;
68 i--;
69 s.ReplaceOneCharAtPos(i, '_');
70 // s.ReplaceOneCharAtPos(i, (c == ' ' ? (wchar_t)(0x2423) : (wchar_t)0x00B7));
71 }
72 /*
73 if (g_PathTrailReplaceMode > 1 && i != s.Len())
74 {
75 s.DeleteFrom(i);
76 }
77 */
78 }
79 }
80}
81#endif
82
83/* WinXP-64 doesn't support ':', '\\' and '/' symbols in name of alt stream.
84 But colon in postfix ":$DATA" is allowed.
85 WIN32 functions don't allow empty alt stream name "name:" */
86
87void Correct_AltStream_Name(UString &s)
88{
89 unsigned len = s.Len();
90 const unsigned kPostfixSize = 6;
91 if (s.Len() >= kPostfixSize
92 && StringsAreEqualNoCase_Ascii(s.RightPtr(kPostfixSize), ":$DATA"))
93 len -= kPostfixSize;
94 for (unsigned i = 0; i < len; i++)
95 {
96 wchar_t c = s[i];
97 if (c == ':' || c == '\\' || c == '/'
98 || c == 0x202E // RLO
99 )
100 s.ReplaceOneCharAtPos(i, '_');
101 }
102 if (s.IsEmpty())
103 s = '_';
104}
105
106#ifdef _WIN32
107
108static const unsigned g_ReservedWithNum_Index = 4;
109
110static const char * const g_ReservedNames[] =
111{
112 "CON", "PRN", "AUX", "NUL",
113 "COM", "LPT"
114};
115
116static bool IsSupportedName(const UString &name)
117{
118 for (unsigned i = 0; i < ARRAY_SIZE(g_ReservedNames); i++)
119 {
120 const char *reservedName = g_ReservedNames[i];
121 unsigned len = MyStringLen(reservedName);
122 if (name.Len() < len)
123 continue;
124 if (!name.IsPrefixedBy_Ascii_NoCase(reservedName))
125 continue;
126 if (i >= g_ReservedWithNum_Index)
127 {
128 wchar_t c = name[len];
129 if (c < L'0' || c > L'9')
130 continue;
131 len++;
132 }
133 for (;;)
134 {
135 wchar_t c = name[len++];
136 if (c == 0 || c == '.')
137 return false;
138 if (c != ' ')
139 break;
140 }
141 }
142 return true;
143}
144
145static void CorrectUnsupportedName(UString &name)
146{
147 if (!IsSupportedName(name))
148 name.InsertAtFront(L'_');
149}
150
151#endif
152
153static void Correct_PathPart(UString &s)
154{
155 // "." and ".."
156 if (s.IsEmpty())
157 return;
158
159 if (s[0] == '.' && (s[1] == 0 || (s[1] == '.' && s[2] == 0)))
160 s.Empty();
161 #ifdef _WIN32
162 else
163 ReplaceIncorrectChars(s);
164 #endif
165}
166
167// static const char * const k_EmptyReplaceName = "[]";
168static const char k_EmptyReplaceName = '_';
169
170UString Get_Correct_FsFile_Name(const UString &name)
171{
172 UString res = name;
173 Correct_PathPart(res);
174
175 #ifdef _WIN32
176 CorrectUnsupportedName(res);
177 #endif
178
179 if (res.IsEmpty())
180 res = k_EmptyReplaceName;
181 return res;
182}
183
184
185void Correct_FsPath(bool absIsAllowed, bool keepAndReplaceEmptyPrefixes, UStringVector &parts, bool isDir)
186{
187 unsigned i = 0;
188
189 if (absIsAllowed)
190 {
191 #if defined(_WIN32) && !defined(UNDER_CE)
192 bool isDrive = false;
193 #endif
194
195 if (parts[0].IsEmpty())
196 {
197 i = 1;
198 #if defined(_WIN32) && !defined(UNDER_CE)
199 if (parts.Size() > 1 && parts[1].IsEmpty())
200 {
201 i = 2;
202 if (parts.Size() > 2 && parts[2] == L"?")
203 {
204 i = 3;
205 if (parts.Size() > 3 && NWindows::NFile::NName::IsDrivePath2(parts[3]))
206 {
207 isDrive = true;
208 i = 4;
209 }
210 }
211 }
212 #endif
213 }
214 #if defined(_WIN32) && !defined(UNDER_CE)
215 else if (NWindows::NFile::NName::IsDrivePath2(parts[0]))
216 {
217 isDrive = true;
218 i = 1;
219 }
220
221 if (isDrive)
222 {
223 // we convert "c:name" to "c:\name", if absIsAllowed path.
224 UString &ds = parts[i - 1];
225 if (ds.Len() > 2)
226 {
227 parts.Insert(i, ds.Ptr(2));
228 ds.DeleteFrom(2);
229 }
230 }
231 #endif
232 }
233
234 if (i != 0)
235 keepAndReplaceEmptyPrefixes = false;
236
237 for (; i < parts.Size();)
238 {
239 UString &s = parts[i];
240
241 Correct_PathPart(s);
242
243 if (s.IsEmpty())
244 {
245 if (!keepAndReplaceEmptyPrefixes)
246 if (isDir || i != parts.Size() - 1)
247 {
248 parts.Delete(i);
249 continue;
250 }
251 s = k_EmptyReplaceName;
252 }
253 else
254 {
255 keepAndReplaceEmptyPrefixes = false;
256 #ifdef _WIN32
257 CorrectUnsupportedName(s);
258 #endif
259 }
260
261 i++;
262 }
263
264 if (!isDir)
265 {
266 if (parts.IsEmpty())
267 parts.Add((UString)k_EmptyReplaceName);
268 else
269 {
270 UString &s = parts.Back();
271 if (s.IsEmpty())
272 s = k_EmptyReplaceName;
273 }
274 }
275}
276
277UString MakePathFromParts(const UStringVector &parts)
278{
279 UString s;
280 FOR_VECTOR (i, parts)
281 {
282 if (i != 0)
283 s.Add_PathSepar();
284 s += parts[i];
285 }
286 return s;
287}
diff --git a/CPP/7zip/UI/Common/ExtractingFilePath.h b/CPP/7zip/UI/Common/ExtractingFilePath.h
new file mode 100644
index 0000000..8f8f9f1
--- /dev/null
+++ b/CPP/7zip/UI/Common/ExtractingFilePath.h
@@ -0,0 +1,31 @@
1// ExtractingFilePath.h
2
3#ifndef __EXTRACTING_FILE_PATH_H
4#define __EXTRACTING_FILE_PATH_H
5
6#include "../../../Common/MyString.h"
7
8// #ifdef _WIN32
9void Correct_AltStream_Name(UString &s);
10// #endif
11
12// replaces unsuported characters, and replaces "." , ".." and "" to "[]"
13UString Get_Correct_FsFile_Name(const UString &name);
14
15/*
16 Correct_FsPath() corrects path parts to prepare it for File System operations.
17 It also corrects empty path parts like "\\\\":
18 - frontal empty path parts : it removes them or changes them to "_"
19 - another empty path parts : it removes them
20 if (absIsAllowed && path is absolute) : it removes empty path parts after start absolute path prefix marker
21 else
22 {
23 if (!keepAndReplaceEmptyPrefixes) : it removes empty path parts
24 if ( keepAndReplaceEmptyPrefixes) : it changes each empty frontal path part to "_"
25 }
26*/
27void Correct_FsPath(bool absIsAllowed, bool keepAndReplaceEmptyPrefixes, UStringVector &parts, bool isDir);
28
29UString MakePathFromParts(const UStringVector &parts);
30
31#endif
diff --git a/CPP/7zip/UI/Common/HashCalc.cpp b/CPP/7zip/UI/Common/HashCalc.cpp
new file mode 100644
index 0000000..2417708
--- /dev/null
+++ b/CPP/7zip/UI/Common/HashCalc.cpp
@@ -0,0 +1,2045 @@
1// HashCalc.cpp
2
3#include "StdAfx.h"
4
5#include "../../../../C/Alloc.h"
6#include "../../../../C/CpuArch.h"
7
8#include "../../../Common/DynLimBuf.h"
9#include "../../../Common/IntToString.h"
10#include "../../../Common/StringToInt.h"
11
12#include "../../Common/FileStreams.h"
13#include "../../Common/ProgressUtils.h"
14#include "../../Common/StreamObjects.h"
15#include "../../Common/StreamUtils.h"
16
17#include "../../Archive/Common/ItemNameUtils.h"
18
19#include "EnumDirItems.h"
20#include "HashCalc.h"
21
22using namespace NWindows;
23
24#ifdef EXTERNAL_CODECS
25extern const CExternalCodecs *g_ExternalCodecs_Ptr;
26#endif
27
28class CHashMidBuf
29{
30 void *_data;
31public:
32 CHashMidBuf(): _data(NULL) {}
33 operator void *() { return _data; }
34 bool Alloc(size_t size)
35 {
36 if (_data)
37 return false;
38 _data = ::MidAlloc(size);
39 return _data != NULL;
40 }
41 ~CHashMidBuf() { ::MidFree(_data); }
42};
43
44static const char * const k_DefaultHashMethod = "CRC32";
45
46HRESULT CHashBundle::SetMethods(DECL_EXTERNAL_CODECS_LOC_VARS const UStringVector &hashMethods)
47{
48 UStringVector names = hashMethods;
49 if (names.IsEmpty())
50 names.Add(UString(k_DefaultHashMethod));
51
52 CRecordVector<CMethodId> ids;
53 CObjectVector<COneMethodInfo> methods;
54
55 unsigned i;
56 for (i = 0; i < names.Size(); i++)
57 {
58 COneMethodInfo m;
59 RINOK(m.ParseMethodFromString(names[i]));
60
61 if (m.MethodName.IsEmpty())
62 m.MethodName = k_DefaultHashMethod;
63
64 if (m.MethodName == "*")
65 {
66 CRecordVector<CMethodId> tempMethods;
67 GetHashMethods(EXTERNAL_CODECS_LOC_VARS tempMethods);
68 methods.Clear();
69 ids.Clear();
70 FOR_VECTOR (t, tempMethods)
71 {
72 unsigned index = ids.AddToUniqueSorted(tempMethods[t]);
73 if (ids.Size() != methods.Size())
74 methods.Insert(index, m);
75 }
76 break;
77 }
78 else
79 {
80 // m.MethodName.RemoveChar(L'-');
81 CMethodId id;
82 if (!FindHashMethod(EXTERNAL_CODECS_LOC_VARS m.MethodName, id))
83 return E_NOTIMPL;
84 unsigned index = ids.AddToUniqueSorted(id);
85 if (ids.Size() != methods.Size())
86 methods.Insert(index, m);
87 }
88 }
89
90 for (i = 0; i < ids.Size(); i++)
91 {
92 CMyComPtr<IHasher> hasher;
93 AString name;
94 RINOK(CreateHasher(EXTERNAL_CODECS_LOC_VARS ids[i], name, hasher));
95 if (!hasher)
96 throw "Can't create hasher";
97 const COneMethodInfo &m = methods[i];
98 {
99 CMyComPtr<ICompressSetCoderProperties> scp;
100 hasher.QueryInterface(IID_ICompressSetCoderProperties, &scp);
101 if (scp)
102 RINOK(m.SetCoderProps(scp, NULL));
103 }
104 const UInt32 digestSize = hasher->GetDigestSize();
105 if (digestSize > k_HashCalc_DigestSize_Max)
106 return E_NOTIMPL;
107 CHasherState &h = Hashers.AddNew();
108 h.DigestSize = digestSize;
109 h.Hasher = hasher;
110 h.Name = name;
111 for (unsigned k = 0; k < k_HashCalc_NumGroups; k++)
112 h.InitDigestGroup(k);
113 }
114
115 return S_OK;
116}
117
118void CHashBundle::InitForNewFile()
119{
120 CurSize = 0;
121 FOR_VECTOR (i, Hashers)
122 {
123 CHasherState &h = Hashers[i];
124 h.Hasher->Init();
125 h.InitDigestGroup(k_HashCalc_Index_Current);
126 }
127}
128
129void CHashBundle::Update(const void *data, UInt32 size)
130{
131 CurSize += size;
132 FOR_VECTOR (i, Hashers)
133 Hashers[i].Hasher->Update(data, size);
134}
135
136void CHashBundle::SetSize(UInt64 size)
137{
138 CurSize = size;
139}
140
141static void AddDigests(Byte *dest, const Byte *src, UInt32 size)
142{
143 unsigned next = 0;
144 /*
145 // we could use big-endian addition for sha-1 and sha-256
146 // but another hashers are little-endian
147 if (size > 8)
148 {
149 for (unsigned i = size; i != 0;)
150 {
151 i--;
152 next += (unsigned)dest[i] + (unsigned)src[i];
153 dest[i] = (Byte)next;
154 next >>= 8;
155 }
156 }
157 else
158 */
159 {
160 for (unsigned i = 0; i < size; i++)
161 {
162 next += (unsigned)dest[i] + (unsigned)src[i];
163 dest[i] = (Byte)next;
164 next >>= 8;
165 }
166 }
167
168 // we use little-endian to store extra bytes
169 dest += k_HashCalc_DigestSize_Max;
170 for (unsigned i = 0; i < k_HashCalc_ExtraSize; i++)
171 {
172 next += (unsigned)dest[i];
173 dest[i] = (Byte)next;
174 next >>= 8;
175 }
176}
177
178void CHasherState::AddDigest(unsigned groupIndex, const Byte *data)
179{
180 NumSums[groupIndex]++;
181 AddDigests(Digests[groupIndex], data, DigestSize);
182}
183
184void CHashBundle::Final(bool isDir, bool isAltStream, const UString &path)
185{
186 if (isDir)
187 NumDirs++;
188 else if (isAltStream)
189 {
190 NumAltStreams++;
191 AltStreamsSize += CurSize;
192 }
193 else
194 {
195 NumFiles++;
196 FilesSize += CurSize;
197 }
198
199 Byte pre[16];
200 memset(pre, 0, sizeof(pre));
201 if (isDir)
202 pre[0] = 1;
203
204 FOR_VECTOR (i, Hashers)
205 {
206 CHasherState &h = Hashers[i];
207 if (!isDir)
208 {
209 h.Hasher->Final(h.Digests[0]); // k_HashCalc_Index_Current
210 if (!isAltStream)
211 h.AddDigest(k_HashCalc_Index_DataSum, h.Digests[0]);
212 }
213
214 h.Hasher->Init();
215 h.Hasher->Update(pre, sizeof(pre));
216 h.Hasher->Update(h.Digests[0], h.DigestSize);
217
218 for (unsigned k = 0; k < path.Len(); k++)
219 {
220 wchar_t c = path[k];
221
222 // 21.04: we want same hash for linux and windows paths
223 #if CHAR_PATH_SEPARATOR != '/'
224 if (c == CHAR_PATH_SEPARATOR)
225 c = '/';
226 // if (c == (wchar_t)('\\' + 0xf000)) c = '\\'; // to debug WSL
227 // if (c > 0xf000 && c < 0xf080) c -= 0xf000; // to debug WSL
228 #endif
229
230 Byte temp[2] = { (Byte)(c & 0xFF), (Byte)((c >> 8) & 0xFF) };
231 h.Hasher->Update(temp, 2);
232 }
233
234 Byte tempDigest[k_HashCalc_DigestSize_Max];
235 h.Hasher->Final(tempDigest);
236 if (!isAltStream)
237 h.AddDigest(k_HashCalc_Index_NamesSum, tempDigest);
238 h.AddDigest(k_HashCalc_Index_StreamsSum, tempDigest);
239 }
240}
241
242
243static void CSum_Name_OriginalToEscape(const AString &src, AString &dest)
244{
245 dest.Empty();
246 for (unsigned i = 0; i < src.Len();)
247 {
248 char c = src[i++];
249 if (c == '\n')
250 {
251 dest += '\\';
252 c = 'n';
253 }
254 else if (c == '\\')
255 dest += '\\';
256 dest += c;
257 }
258}
259
260
261static bool CSum_Name_EscapeToOriginal(const char *s, AString &dest)
262{
263 bool isOK = true;
264 dest.Empty();
265 for (;;)
266 {
267 char c = *s++;
268 if (c == 0)
269 break;
270 if (c == '\\')
271 {
272 const char c1 = *s;
273 if (c1 == 'n')
274 {
275 c = '\n';
276 s++;
277 }
278 else if (c1 == '\\')
279 {
280 c = c1;
281 s++;
282 }
283 else
284 {
285 // original md5sum returns NULL for such bad strings
286 isOK = false;
287 }
288 }
289 dest += c;
290 }
291 return isOK;
292}
293
294
295
296static void SetSpacesAndNul(char *s, unsigned num)
297{
298 for (unsigned i = 0; i < num; i++)
299 s[i] = ' ';
300 s[num] = 0;
301}
302
303static const unsigned kHashColumnWidth_Min = 4 * 2;
304
305static unsigned GetColumnWidth(unsigned digestSize)
306{
307 const unsigned width = digestSize * 2;
308 return width < kHashColumnWidth_Min ? kHashColumnWidth_Min: width;
309}
310
311
312void HashHexToString(char *dest, const Byte *data, UInt32 size);
313
314static void AddHashResultLine(
315 AString &_s,
316 // bool showHash,
317 // UInt64 fileSize, bool showSize,
318 const CObjectVector<CHasherState> &hashers
319 // unsigned digestIndex, = k_HashCalc_Index_Current
320 )
321{
322 FOR_VECTOR (i, hashers)
323 {
324 const CHasherState &h = hashers[i];
325 char s[k_HashCalc_DigestSize_Max * 2 + 64];
326 s[0] = 0;
327 // if (showHash)
328 HashHexToString(s, h.Digests[k_HashCalc_Index_Current], h.DigestSize);
329 const unsigned pos = (unsigned)strlen(s);
330 const int numSpaces = (int)GetColumnWidth(h.DigestSize) - (int)pos;
331 if (numSpaces > 0)
332 SetSpacesAndNul(s + pos, (unsigned)numSpaces);
333 if (i != 0)
334 _s += ' ';
335 _s += s;
336 }
337
338 /*
339 if (showSize)
340 {
341 _s += ' ';
342 static const unsigned kSizeField_Len = 13; // same as in HashCon.cpp
343 char s[kSizeField_Len + 32];
344 char *p = s;
345 SetSpacesAndNul(s, kSizeField_Len);
346 p = s + kSizeField_Len;
347 ConvertUInt64ToString(fileSize, p);
348 int numSpaces = (int)kSizeField_Len - (int)strlen(p);
349 if (numSpaces > 0)
350 p -= (unsigned)numSpaces;
351 _s += p;
352 }
353 */
354}
355
356
357static void Add_LF(CDynLimBuf &hashFileString, const CHashOptionsLocal &options)
358{
359 hashFileString += (char)(options.HashMode_Zero.Val ? 0 : '\n');
360}
361
362
363
364
365static void WriteLine(CDynLimBuf &hashFileString,
366 const CHashOptionsLocal &options,
367 const UString &path2,
368 bool isDir,
369 const AString &methodName,
370 const AString &hashesString)
371{
372 if (options.HashMode_OnlyHash.Val)
373 {
374 hashFileString += hashesString;
375 Add_LF(hashFileString, options);
376 return;
377 }
378
379 UString path = path2;
380
381 bool isBin = false;
382 const bool zeroMode = options.HashMode_Zero.Val;
383 const bool tagMode = options.HashMode_Tag.Val;
384
385#if CHAR_PATH_SEPARATOR != '/'
386 path.Replace(WCHAR_PATH_SEPARATOR, L'/');
387 // path.Replace((wchar_t)('\\' + 0xf000), L'\\'); // to debug WSL
388#endif
389
390 AString utf8;
391 ConvertUnicodeToUTF8(path, utf8);
392
393 AString esc;
394 CSum_Name_OriginalToEscape(utf8, esc);
395
396 if (!zeroMode)
397 {
398 if (esc != utf8)
399 {
400 /* Original md5sum writes escape in that case.
401 We do same for compatibility with original md5sum. */
402 hashFileString += '\\';
403 }
404 }
405
406 if (isDir && !esc.IsEmpty() && esc.Back() != '/')
407 esc += '/';
408
409 if (tagMode)
410 {
411 if (!methodName.IsEmpty())
412 {
413 hashFileString += methodName;
414 hashFileString += ' ';
415 }
416 hashFileString += '(';
417 hashFileString += esc;
418 hashFileString += ')';
419 hashFileString += " = ";
420 }
421
422 hashFileString += hashesString;
423
424 if (!tagMode)
425 {
426 hashFileString += ' ';
427 hashFileString += (char)(isBin ? '*' : ' ');
428 hashFileString += esc;
429 }
430
431 Add_LF(hashFileString, options);
432}
433
434
435
436static void WriteLine(CDynLimBuf &hashFileString,
437 const CHashOptionsLocal &options,
438 const UString &path,
439 bool isDir,
440 const CHashBundle &hb)
441{
442 AString methodName;
443 if (!hb.Hashers.IsEmpty())
444 methodName = hb.Hashers[0].Name;
445
446 AString hashesString;
447 AddHashResultLine(hashesString, hb.Hashers);
448 WriteLine(hashFileString, options, path, isDir, methodName, hashesString);
449}
450
451
452HRESULT HashCalc(
453 DECL_EXTERNAL_CODECS_LOC_VARS
454 const NWildcard::CCensor &censor,
455 const CHashOptions &options,
456 AString &errorInfo,
457 IHashCallbackUI *callback)
458{
459 CDirItems dirItems;
460 dirItems.Callback = callback;
461
462 if (options.StdInMode)
463 {
464 CDirItem di;
465 di.Size = (UInt64)(Int64)-1;
466 di.Attrib = 0;
467 di.MTime.dwLowDateTime = 0;
468 di.MTime.dwHighDateTime = 0;
469 di.CTime = di.ATime = di.MTime;
470 dirItems.Items.Add(di);
471 }
472 else
473 {
474 RINOK(callback->StartScanning());
475
476 dirItems.SymLinks = options.SymLinks.Val;
477 dirItems.ScanAltStreams = options.AltStreamsMode;
478 dirItems.ExcludeDirItems = censor.ExcludeDirItems;
479 dirItems.ExcludeFileItems = censor.ExcludeFileItems;
480
481 HRESULT res = EnumerateItems(censor,
482 options.PathMode,
483 UString(),
484 dirItems);
485
486 if (res != S_OK)
487 {
488 if (res != E_ABORT)
489 errorInfo = "Scanning error";
490 return res;
491 }
492 RINOK(callback->FinishScanning(dirItems.Stat));
493 }
494
495 unsigned i;
496 CHashBundle hb;
497 RINOK(hb.SetMethods(EXTERNAL_CODECS_LOC_VARS options.Methods));
498 // hb.Init();
499
500 hb.NumErrors = dirItems.Stat.NumErrors;
501
502 if (options.StdInMode)
503 {
504 RINOK(callback->SetNumFiles(1));
505 }
506 else
507 {
508 RINOK(callback->SetTotal(dirItems.Stat.GetTotalBytes()));
509 }
510
511 const UInt32 kBufSize = 1 << 15;
512 CHashMidBuf buf;
513 if (!buf.Alloc(kBufSize))
514 return E_OUTOFMEMORY;
515
516 UInt64 completeValue = 0;
517
518 RINOK(callback->BeforeFirstFile(hb));
519
520 /*
521 CDynLimBuf hashFileString((size_t)1 << 31);
522 const bool needGenerate = !options.HashFilePath.IsEmpty();
523 */
524
525 for (i = 0; i < dirItems.Items.Size(); i++)
526 {
527 CMyComPtr<ISequentialInStream> inStream;
528 UString path;
529 bool isDir = false;
530 bool isAltStream = false;
531
532 if (options.StdInMode)
533 {
534 inStream = new CStdInFileStream;
535 }
536 else
537 {
538 path = dirItems.GetLogPath(i);
539 const CDirItem &di = dirItems.Items[i];
540 isAltStream = di.IsAltStream;
541
542 #ifndef UNDER_CE
543 // if (di.AreReparseData())
544 if (di.ReparseData.Size() != 0)
545 {
546 CBufInStream *inStreamSpec = new CBufInStream();
547 inStream = inStreamSpec;
548 inStreamSpec->Init(di.ReparseData, di.ReparseData.Size());
549 }
550 else
551 #endif
552 {
553 CInFileStream *inStreamSpec = new CInFileStream;
554 inStreamSpec->File.PreserveATime = options.PreserveATime;
555 inStream = inStreamSpec;
556 isDir = di.IsDir();
557 if (!isDir)
558 {
559 const FString phyPath = dirItems.GetPhyPath(i);
560 if (!inStreamSpec->OpenShared(phyPath, options.OpenShareForWrite))
561 {
562 HRESULT res = callback->OpenFileError(phyPath, ::GetLastError());
563 hb.NumErrors++;
564 if (res != S_FALSE)
565 return res;
566 continue;
567 }
568 }
569 }
570 }
571
572 RINOK(callback->GetStream(path, isDir));
573 UInt64 fileSize = 0;
574
575 hb.InitForNewFile();
576
577 if (!isDir)
578 {
579 for (UInt32 step = 0;; step++)
580 {
581 if ((step & 0xFF) == 0)
582 {
583 RINOK(callback->SetCompleted(&completeValue));
584 }
585 UInt32 size;
586 RINOK(inStream->Read(buf, kBufSize, &size));
587 if (size == 0)
588 break;
589 hb.Update(buf, size);
590 fileSize += size;
591 completeValue += size;
592 }
593 }
594
595 hb.Final(isDir, isAltStream, path);
596
597 /*
598 if (needGenerate
599 && (options.HashMode_Dirs.Val || !isDir))
600 {
601 WriteLine(hashFileString,
602 options,
603 path, // change it
604 isDir,
605 hb);
606
607 if (hashFileString.IsError())
608 return E_OUTOFMEMORY;
609 }
610 */
611
612 RINOK(callback->SetOperationResult(fileSize, hb, !isDir));
613 RINOK(callback->SetCompleted(&completeValue));
614 }
615
616 /*
617 if (needGenerate)
618 {
619 NFile::NIO::COutFile file;
620 if (!file.Create(us2fs(options.HashFilePath), true)) // createAlways
621 return GetLastError_noZero_HRESULT();
622 if (!file.WriteFull(hashFileString, hashFileString.Len()))
623 return GetLastError_noZero_HRESULT();
624 }
625 */
626
627 return callback->AfterLastFile(hb);
628}
629
630
631static inline char GetHex_Upper(unsigned v)
632{
633 return (char)((v < 10) ? ('0' + v) : ('A' + (v - 10)));
634}
635
636static inline char GetHex_Lower(unsigned v)
637{
638 return (char)((v < 10) ? ('0' + v) : ('a' + (v - 10)));
639}
640
641void HashHexToString(char *dest, const Byte *data, UInt32 size)
642{
643 dest[size * 2] = 0;
644
645 if (!data)
646 {
647 for (UInt32 i = 0; i < size; i++)
648 {
649 dest[0] = ' ';
650 dest[1] = ' ';
651 dest += 2;
652 }
653 return;
654 }
655
656 if (size <= 8)
657 {
658 dest += size * 2;
659 for (UInt32 i = 0; i < size; i++)
660 {
661 const unsigned b = data[i];
662 dest -= 2;
663 dest[0] = GetHex_Upper((b >> 4) & 0xF);
664 dest[1] = GetHex_Upper(b & 0xF);
665 }
666 }
667 else
668 {
669 for (UInt32 i = 0; i < size; i++)
670 {
671 const unsigned b = data[i];
672 dest[0] = GetHex_Lower((b >> 4) & 0xF);
673 dest[1] = GetHex_Lower(b & 0xF);
674 dest += 2;
675 }
676 }
677}
678
679void CHasherState::WriteToString(unsigned digestIndex, char *s) const
680{
681 HashHexToString(s, Digests[digestIndex], DigestSize);
682
683 if (digestIndex != 0 && NumSums[digestIndex] != 1)
684 {
685 unsigned numExtraBytes = GetNumExtraBytes_for_Group(digestIndex);
686 if (numExtraBytes > 4)
687 numExtraBytes = 8;
688 else // if (numExtraBytes >= 0)
689 numExtraBytes = 4;
690 // if (numExtraBytes != 0)
691 {
692 s += strlen(s);
693 *s++ = '-';
694 // *s = 0;
695 HashHexToString(s, GetExtraData_for_Group(digestIndex), numExtraBytes);
696 }
697 }
698}
699
700
701
702// ---------- Hash Handler ----------
703
704namespace NHash {
705
706static size_t ParseHexString(const char *s, Byte *dest) throw()
707{
708 size_t num;
709 for (num = 0;; num++, s += 2)
710 {
711 unsigned c = (Byte)s[0];
712 unsigned v0;
713 if (c >= '0' && c <= '9') v0 = (c - '0');
714 else if (c >= 'A' && c <= 'F') v0 = 10 + (c - 'A');
715 else if (c >= 'a' && c <= 'f') v0 = 10 + (c - 'a');
716 else
717 return num;
718 c = (Byte)s[1];
719 unsigned v1;
720 if (c >= '0' && c <= '9') v1 = (c - '0');
721 else if (c >= 'A' && c <= 'F') v1 = 10 + (c - 'A');
722 else if (c >= 'a' && c <= 'f') v1 = 10 + (c - 'a');
723 else
724 return num;
725 if (dest)
726 dest[num] = (Byte)(v1 | (v0 << 4));
727 }
728}
729
730
731#define IsWhite(c) ((c) == ' ' || (c) == '\t')
732
733bool CHashPair::IsDir() const
734{
735 if (Name.IsEmpty() || Name.Back() != '/')
736 return false;
737 // here we expect that Dir items contain only zeros or no Hash
738 for (size_t i = 0; i < Hash.Size(); i++)
739 if (Hash[i] != 0)
740 return false;
741 return true;
742}
743
744
745bool CHashPair::ParseCksum(const char *s)
746{
747 const char *end;
748
749 const UInt32 crc = ConvertStringToUInt32(s, &end);
750 if (*end != ' ')
751 return false;
752 end++;
753
754 const UInt64 size = ConvertStringToUInt64(end, &end);
755 if (*end != ' ')
756 return false;
757 end++;
758
759 Name = end;
760
761 Hash.Alloc(4);
762 SetBe32(Hash, crc);
763
764 Size_from_Arc = size;
765 Size_from_Arc_Defined = true;
766
767 return true;
768}
769
770
771
772static const char *SkipWhite(const char *s)
773{
774 while (IsWhite(*s))
775 s++;
776 return s;
777}
778
779static const char * const k_CsumMethodNames[] =
780{
781 "sha256"
782 , "sha224"
783// , "sha512/224"
784// , "sha512/256"
785 , "sha512"
786 , "sha384"
787 , "sha1"
788 , "md5"
789 , "blake2b"
790 , "crc64"
791 , "crc32"
792 , "cksum"
793};
794
795static UString GetMethod_from_FileName(const UString &name)
796{
797 AString s;
798 ConvertUnicodeToUTF8(name, s);
799 const int dotPos = s.ReverseFind_Dot();
800 const char *src = s.Ptr();
801 bool isExtension = false;
802 if (dotPos >= 0)
803 {
804 isExtension = true;
805 src = s.Ptr(dotPos + 1);
806 }
807 const char *m = "";
808 unsigned i;
809 for (i = 0; i < ARRAY_SIZE(k_CsumMethodNames); i++)
810 {
811 m = k_CsumMethodNames[i];
812 if (isExtension)
813 {
814 if (StringsAreEqual_Ascii(src, m))
815 break;
816 }
817 else if (IsString1PrefixedByString2_NoCase_Ascii(src, m))
818 if (StringsAreEqual_Ascii(src + strlen(m), "sums"))
819 break;
820 }
821 UString res;
822 if (i != ARRAY_SIZE(k_CsumMethodNames))
823 res = m;
824 return res;
825}
826
827
828bool CHashPair::Parse(const char *s)
829{
830 // here we keep compatibility with original md5sum / shasum
831 bool escape = false;
832
833 s = SkipWhite(s);
834
835 if (*s == '\\')
836 {
837 s++;
838 escape = true;
839 }
840
841 // const char *kMethod = GetMethod_from_FileName(s);
842 // if (kMethod)
843 if (ParseHexString(s, NULL) < 4)
844 {
845 // BSD-style checksum line
846 {
847 const char *s2 = s;
848 for (; *s2 != 0; s2++)
849 {
850 const char c = *s2;
851 if (c == 0)
852 return false;
853 if (c == ' ' || c == '(')
854 break;
855 }
856 Method.SetFrom(s, (unsigned)(s2 - s));
857 s = s2;
858 }
859 IsBSD = true;
860 if (*s == ' ')
861 s++;
862 if (*s != '(')
863 return false;
864 s++;
865 {
866 const char *s2 = s;
867 for (; *s2 != 0; s2++)
868 {}
869 for (;;)
870 {
871 s2--;
872 if (s2 < s)
873 return false;
874 if (*s2 == ')')
875 break;
876 }
877 Name.SetFrom(s, (unsigned)(s2 - s));
878 s = s2 + 1;
879 }
880
881 s = SkipWhite(s);
882 if (*s != '=')
883 return false;
884 s++;
885 s = SkipWhite(s);
886 }
887
888 {
889 const size_t num = ParseHexString(s, NULL);
890 Hash.Alloc(num);
891 ParseHexString(s, Hash);
892 const size_t numChars = num * 2;
893 HashString.SetFrom(s, (unsigned)numChars);
894 s += numChars;
895 }
896
897 if (IsBSD)
898 {
899 if (*s != 0)
900 return false;
901 if (escape)
902 {
903 const AString temp (Name);
904 return CSum_Name_EscapeToOriginal(temp, Name);
905 }
906 return true;
907 }
908
909 if (*s == 0)
910 return true;
911
912 if (*s != ' ')
913 return false;
914 s++;
915 const char c = *s;
916 if (c != ' '
917 && c != '*'
918 && c != 'U' // shasum Universal
919 && c != '^' // shasum 0/1
920 )
921 return false;
922 Mode = c;
923 s++;
924 if (escape)
925 return CSum_Name_EscapeToOriginal(s, Name);
926 Name = s;
927 return true;
928}
929
930
931static bool GetLine(CByteBuffer &buf, bool zeroMode, bool cr_lf_Mode, size_t &posCur, AString &s)
932{
933 s.Empty();
934 size_t pos = posCur;
935 const Byte *p = buf;
936 unsigned numDigits = 0;
937 for (; pos < buf.Size(); pos++)
938 {
939 const Byte b = p[pos];
940 if (b == 0)
941 {
942 numDigits = 1;
943 break;
944 }
945 if (zeroMode)
946 continue;
947 if (b == 0x0a)
948 {
949 numDigits = 1;
950 break;
951 }
952 if (!cr_lf_Mode)
953 continue;
954 if (b == 0x0d)
955 {
956 if (pos + 1 >= buf.Size())
957 {
958 numDigits = 1;
959 break;
960 // return false;
961 }
962 if (p[pos + 1] == 0x0a)
963 {
964 numDigits = 2;
965 break;
966 }
967 }
968 }
969 s.SetFrom((const char *)(p + posCur), (unsigned)(pos - posCur));
970 posCur = pos + numDigits;
971 return true;
972}
973
974
975static bool Is_CR_LF_Data(const Byte *buf, size_t size)
976{
977 bool isCrLf = false;
978 for (size_t i = 0; i < size;)
979 {
980 const Byte b = buf[i];
981 if (b == 0x0a)
982 return false;
983 if (b == 0x0d)
984 {
985 if (i == size - 1)
986 return false;
987 if (buf[i + 1] != 0x0a)
988 return false;
989 isCrLf = true;
990 i += 2;
991 }
992 else
993 i++;
994 }
995 return isCrLf;
996}
997
998
999static const Byte kArcProps[] =
1000{
1001 // kpidComment,
1002 kpidCharacts
1003};
1004
1005static const Byte kProps[] =
1006{
1007 kpidPath,
1008 kpidSize,
1009 kpidPackSize,
1010 kpidMethod
1011};
1012
1013static const Byte kRawProps[] =
1014{
1015 kpidChecksum
1016};
1017
1018
1019STDMETHODIMP CHandler::GetParent(UInt32 /* index */ , UInt32 *parent, UInt32 *parentType)
1020{
1021 *parentType = NParentType::kDir;
1022 *parent = (UInt32)(Int32)-1;
1023 return S_OK;
1024}
1025
1026STDMETHODIMP CHandler::GetNumRawProps(UInt32 *numProps)
1027{
1028 *numProps = ARRAY_SIZE(kRawProps);
1029 return S_OK;
1030}
1031
1032STDMETHODIMP CHandler::GetRawPropInfo(UInt32 index, BSTR *name, PROPID *propID)
1033{
1034 *propID = kRawProps[index];
1035 *name = 0;
1036 return S_OK;
1037}
1038
1039STDMETHODIMP CHandler::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType)
1040{
1041 *data = NULL;
1042 *dataSize = 0;
1043 *propType = 0;
1044
1045 if (propID == kpidChecksum)
1046 {
1047 const CHashPair &hp = HashPairs[index];
1048 if (hp.Hash.Size() > 0)
1049 {
1050 *data = hp.Hash;
1051 *dataSize = (UInt32)hp.Hash.Size();
1052 *propType = NPropDataType::kRaw;
1053 }
1054 return S_OK;
1055 }
1056
1057 return S_OK;
1058}
1059
1060IMP_IInArchive_Props
1061IMP_IInArchive_ArcProps
1062
1063STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
1064{
1065 *numItems = HashPairs.Size();
1066 return S_OK;
1067}
1068
1069static void Add_OptSpace_String(UString &dest, const char *src)
1070{
1071 dest.Add_Space_if_NotEmpty();
1072 dest += src;
1073}
1074
1075STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
1076{
1077 NWindows::NCOM::CPropVariant prop;
1078 switch (propID)
1079 {
1080 case kpidPhySize: if (_phySize != 0) prop = _phySize; break;
1081 /*
1082 case kpidErrorFlags:
1083 {
1084 UInt32 v = 0;
1085 if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;
1086 // if (_sres == k_Base64_RES_NeedMoreInput) v |= kpv_ErrorFlags_UnexpectedEnd;
1087 if (v != 0)
1088 prop = v;
1089 break;
1090 }
1091 */
1092 case kpidCharacts:
1093 {
1094 UString s;
1095 if (_hashSize_Defined)
1096 {
1097 s.Add_Space_if_NotEmpty();
1098 s.Add_UInt32(_hashSize * 8);
1099 s += "-bit";
1100 }
1101 if (!_nameExtenstion.IsEmpty())
1102 {
1103 s.Add_Space_if_NotEmpty();
1104 s += _nameExtenstion;
1105 }
1106 if (_is_PgpMethod)
1107 {
1108 Add_OptSpace_String(s, "PGP");
1109 if (!_pgpMethod.IsEmpty())
1110 {
1111 s += ":";
1112 s += _pgpMethod;
1113 }
1114 }
1115 if (_is_ZeroMode)
1116 Add_OptSpace_String(s, "ZERO");
1117 if (_are_there_Tags)
1118 Add_OptSpace_String(s, "TAG");
1119 if (_are_there_Dirs)
1120 Add_OptSpace_String(s, "DIRS");
1121 prop = s;
1122 break;
1123 }
1124
1125 case kpidReadOnly:
1126 {
1127 if (_isArc)
1128 if (!CanUpdate())
1129 prop = true;
1130 break;
1131 }
1132 }
1133 prop.Detach(value);
1134 return S_OK;
1135}
1136
1137
1138STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
1139{
1140 // COM_TRY_BEGIN
1141 NWindows::NCOM::CPropVariant prop;
1142 CHashPair &hp = HashPairs[index];
1143 switch (propID)
1144 {
1145 case kpidIsDir:
1146 {
1147 prop = hp.IsDir();
1148 break;
1149 }
1150 case kpidPath:
1151 {
1152 UString path;
1153 hp.Get_UString_Path(path);
1154
1155 NArchive::NItemName::ReplaceToOsSlashes_Remove_TailSlash(path,
1156 true); // useBackslashReplacement
1157
1158 prop = path;
1159 break;
1160 }
1161 case kpidSize:
1162 {
1163 // client needs processed size of last file
1164 if (hp.Size_from_Disk_Defined)
1165 prop = (UInt64)hp.Size_from_Disk;
1166 else if (hp.Size_from_Arc_Defined)
1167 prop = (UInt64)hp.Size_from_Arc;
1168 break;
1169 }
1170 case kpidPackSize:
1171 {
1172 prop = (UInt64)hp.Hash.Size();
1173 break;
1174 }
1175 case kpidMethod:
1176 {
1177 if (!hp.Method.IsEmpty())
1178 prop = hp.Method;
1179 break;
1180 }
1181 }
1182 prop.Detach(value);
1183 return S_OK;
1184 // COM_TRY_END
1185}
1186
1187
1188static HRESULT ReadStream_to_Buf(IInStream *stream, CByteBuffer &buf, IArchiveOpenCallback *openCallback)
1189{
1190 buf.Free();
1191 UInt64 len;
1192 RINOK(stream->Seek(0, STREAM_SEEK_END, &len));
1193 if (len == 0 || len >= ((UInt64)1 << 31))
1194 return S_FALSE;
1195 RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL));
1196 buf.Alloc((size_t)len);
1197 UInt64 pos = 0;
1198 // return ReadStream_FALSE(stream, buf, (size_t)len);
1199 for (;;)
1200 {
1201 const UInt32 kBlockSize = ((UInt32)1 << 24);
1202 const UInt32 curSize = (len < kBlockSize) ? (UInt32)len : kBlockSize;
1203 UInt32 processedSizeLoc;
1204 RINOK(stream->Read((Byte *)buf + pos, curSize, &processedSizeLoc));
1205 if (processedSizeLoc == 0)
1206 return E_FAIL;
1207 len -= processedSizeLoc;
1208 pos += processedSizeLoc;
1209 if (len == 0)
1210 return S_OK;
1211 if (openCallback)
1212 {
1213 const UInt64 files = 0;
1214 RINOK(openCallback->SetCompleted(&files, &pos));
1215 }
1216 }
1217}
1218
1219
1220STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *openCallback)
1221{
1222 COM_TRY_BEGIN
1223 {
1224 Close();
1225
1226 CByteBuffer buf;
1227 RINOK(ReadStream_to_Buf(stream, buf, openCallback))
1228
1229 CObjectVector<CHashPair> &pairs = HashPairs;
1230
1231 bool zeroMode = false;
1232 bool cr_lf_Mode = false;
1233 {
1234 for (size_t i = 0; i < buf.Size(); i++)
1235 if (buf[i] == 0)
1236 {
1237 zeroMode = true;
1238 break;
1239 }
1240 }
1241 _is_ZeroMode = zeroMode;
1242 if (!zeroMode)
1243 cr_lf_Mode = Is_CR_LF_Data(buf, buf.Size());
1244
1245 if (openCallback)
1246 {
1247 CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback;
1248 openCallback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&openVolumeCallback);
1249 NCOM::CPropVariant prop;
1250 if (openVolumeCallback)
1251 {
1252 RINOK(openVolumeCallback->GetProperty(kpidName, &prop));
1253 if (prop.vt == VT_BSTR)
1254 _nameExtenstion = GetMethod_from_FileName(prop.bstrVal);
1255 }
1256 }
1257
1258 bool cksumMode = false;
1259 if (_nameExtenstion.IsEqualTo_Ascii_NoCase("cksum"))
1260 cksumMode = true;
1261 _is_CksumMode = cksumMode;
1262
1263 size_t pos = 0;
1264 AString s;
1265 bool minusMode = false;
1266 unsigned numLines = 0;
1267
1268 while (pos < buf.Size())
1269 {
1270 if (!GetLine(buf, zeroMode, cr_lf_Mode, pos, s))
1271 return S_FALSE;
1272 numLines++;
1273 if (s.IsEmpty())
1274 continue;
1275
1276 if (s.IsPrefixedBy_Ascii_NoCase("; "))
1277 {
1278 if (numLines != 1)
1279 return S_FALSE;
1280 // comment line of FileVerifier++
1281 continue;
1282 }
1283
1284 if (s.IsPrefixedBy_Ascii_NoCase("-----"))
1285 {
1286 if (minusMode)
1287 break; // end of pgp mode
1288 minusMode = true;
1289 if (s.IsPrefixedBy_Ascii_NoCase("-----BEGIN PGP SIGNED MESSAGE"))
1290 {
1291 if (_is_PgpMethod)
1292 return S_FALSE;
1293 if (!GetLine(buf, zeroMode, cr_lf_Mode, pos, s))
1294 return S_FALSE;
1295 const char *kStart = "Hash: ";
1296 if (!s.IsPrefixedBy_Ascii_NoCase(kStart))
1297 return S_FALSE;
1298 _pgpMethod = s.Ptr((unsigned)strlen(kStart));
1299 _is_PgpMethod = true;
1300 }
1301 continue;
1302 }
1303
1304 CHashPair pair;
1305 pair.FullLine = s;
1306 if (cksumMode)
1307 {
1308 if (!pair.ParseCksum(s))
1309 return S_FALSE;
1310 }
1311 else if (!pair.Parse(s))
1312 return S_FALSE;
1313 pairs.Add(pair);
1314 }
1315
1316 {
1317 unsigned hashSize = 0;
1318 bool hashSize_Dismatch = false;
1319 for (unsigned i = 0; i < HashPairs.Size(); i++)
1320 {
1321 const CHashPair &hp = HashPairs[i];
1322 if (i == 0)
1323 hashSize = (unsigned)hp.Hash.Size();
1324 else
1325 if (hashSize != hp.Hash.Size())
1326 hashSize_Dismatch = true;
1327
1328 if (hp.IsBSD)
1329 _are_there_Tags = true;
1330 if (!_are_there_Dirs && hp.IsDir())
1331 _are_there_Dirs = true;
1332 }
1333 if (!hashSize_Dismatch && hashSize != 0)
1334 {
1335 _hashSize = hashSize;
1336 _hashSize_Defined = true;
1337 }
1338 }
1339
1340 _phySize = buf.Size();
1341 _isArc = true;
1342 return S_OK;
1343 }
1344 COM_TRY_END
1345}
1346
1347
1348void CHandler::ClearVars()
1349{
1350 _phySize = 0;
1351 _isArc = false;
1352 _is_CksumMode = false;
1353 _is_PgpMethod = false;
1354 _is_ZeroMode = false;
1355 _are_there_Tags = false;
1356 _are_there_Dirs = false;
1357 _hashSize_Defined = false;
1358 _hashSize = 0;
1359}
1360
1361
1362STDMETHODIMP CHandler::Close()
1363{
1364 ClearVars();
1365 _nameExtenstion.Empty();
1366 _pgpMethod.Empty();
1367 HashPairs.Clear();
1368 return S_OK;
1369}
1370
1371
1372static bool CheckDigests(const Byte *a, const Byte *b, size_t size)
1373{
1374 if (size <= 8)
1375 {
1376 /* we use reversed order for one digest, when text representation
1377 uses big-order for crc-32 and crc-64 */
1378 for (size_t i = 0; i < size; i++)
1379 if (a[i] != b[size - 1 - i])
1380 return false;
1381 return true;
1382 }
1383 {
1384 for (size_t i = 0; i < size; i++)
1385 if (a[i] != b[i])
1386 return false;
1387 return true;
1388 }
1389}
1390
1391
1392static void AddDefaultMethod(UStringVector &methods, unsigned size)
1393{
1394 const char *m = NULL;
1395 if (size == 32) m = "sha256";
1396 else if (size == 20) m = "sha1";
1397 else if (size == 16) m = "md5";
1398 else if (size == 8) m = "crc64";
1399 else if (size == 4) m = "crc32";
1400 else
1401 return;
1402 #ifdef EXTERNAL_CODECS
1403 const CExternalCodecs *__externalCodecs = g_ExternalCodecs_Ptr;
1404 #endif
1405 CMethodId id;
1406 if (FindHashMethod(EXTERNAL_CODECS_LOC_VARS
1407 AString(m), id))
1408 methods.Add(UString(m));
1409}
1410
1411
1412STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
1413 Int32 testMode, IArchiveExtractCallback *extractCallback)
1414{
1415 COM_TRY_BEGIN
1416
1417 /*
1418 if (testMode == 0)
1419 return E_NOTIMPL;
1420 */
1421
1422 const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
1423 if (allFilesMode)
1424 numItems = HashPairs.Size();
1425 if (numItems == 0)
1426 return S_OK;
1427
1428 #ifdef EXTERNAL_CODECS
1429 const CExternalCodecs *__externalCodecs = g_ExternalCodecs_Ptr;
1430 #endif
1431
1432 CHashBundle hb_Glob;
1433 // UStringVector methods = options.Methods;
1434 UStringVector methods;
1435
1436 if (methods.IsEmpty() && !_nameExtenstion.IsEmpty())
1437 {
1438 AString utf;
1439 ConvertUnicodeToUTF8(_nameExtenstion, utf);
1440 CMethodId id;
1441 if (FindHashMethod(EXTERNAL_CODECS_LOC_VARS utf, id))
1442 methods.Add(_nameExtenstion);
1443 }
1444
1445 if (methods.IsEmpty() && !_pgpMethod.IsEmpty())
1446 {
1447 CMethodId id;
1448 if (FindHashMethod(EXTERNAL_CODECS_LOC_VARS _pgpMethod, id))
1449 methods.Add(UString(_pgpMethod));
1450 }
1451
1452 if (methods.IsEmpty() && _pgpMethod.IsEmpty() && _hashSize_Defined)
1453 AddDefaultMethod(methods, _hashSize);
1454
1455 RINOK(hb_Glob.SetMethods(
1456 EXTERNAL_CODECS_LOC_VARS
1457 methods));
1458
1459 CMyComPtr<IArchiveUpdateCallbackFile> updateCallbackFile;
1460 extractCallback->QueryInterface(IID_IArchiveUpdateCallbackFile, (void **)&updateCallbackFile);
1461 if (!updateCallbackFile)
1462 return E_NOTIMPL;
1463 {
1464 CMyComPtr<IArchiveGetDiskProperty> GetDiskProperty;
1465 extractCallback->QueryInterface(IID_IArchiveGetDiskProperty, (void **)&GetDiskProperty);
1466 if (GetDiskProperty)
1467 {
1468 UInt64 totalSize = 0;
1469 UInt32 i;
1470 for (i = 0; i < numItems; i++)
1471 {
1472 const UInt32 index = allFilesMode ? i : indices[i];
1473 const CHashPair &hp = HashPairs[index];
1474 if (hp.IsDir())
1475 continue;
1476 {
1477 NCOM::CPropVariant prop;
1478 RINOK(GetDiskProperty->GetDiskProperty(index, kpidSize, &prop));
1479 if (prop.vt != VT_UI8)
1480 continue;
1481 totalSize += prop.uhVal.QuadPart;
1482 }
1483 }
1484 RINOK(extractCallback->SetTotal(totalSize));
1485 // RINOK(Hash_SetTotalUnpacked->Hash_SetTotalUnpacked(indices, numItems));
1486 }
1487 }
1488
1489 const UInt32 kBufSize = 1 << 15;
1490 CHashMidBuf buf;
1491 if (!buf.Alloc(kBufSize))
1492 return E_OUTOFMEMORY;
1493
1494 CLocalProgress *lps = new CLocalProgress;
1495 CMyComPtr<ICompressProgressInfo> progress = lps;
1496 lps->Init(extractCallback, false);
1497 lps->InSize = lps->OutSize = 0;
1498
1499 UInt32 i;
1500 for (i = 0; i < numItems; i++)
1501 {
1502 RINOK(lps->SetCur());
1503 const UInt32 index = allFilesMode ? i : indices[i];
1504
1505 CHashPair &hp = HashPairs[index];
1506
1507 UString path;
1508 hp.Get_UString_Path(path);
1509
1510 CMyComPtr<ISequentialInStream> inStream;
1511 const bool isDir = hp.IsDir();
1512 if (!isDir)
1513 {
1514 RINOK(updateCallbackFile->GetStream2(index, &inStream, NUpdateNotifyOp::kHashRead));
1515 if (!inStream)
1516 {
1517 continue; // we have shown error in GetStream2()
1518 }
1519 // askMode = NArchive::NExtract::NAskMode::kSkip;
1520 }
1521
1522 Int32 askMode = testMode ?
1523 NArchive::NExtract::NAskMode::kTest :
1524 NArchive::NExtract::NAskMode::kExtract;
1525
1526 CMyComPtr<ISequentialOutStream> realOutStream;
1527 RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
1528
1529 /* PrepareOperation() can expect kExtract to set
1530 Attrib and security of output file */
1531 askMode = NArchive::NExtract::NAskMode::kReadExternal;
1532
1533 extractCallback->PrepareOperation(askMode);
1534
1535 const bool isAltStream = false;
1536
1537 UInt64 fileSize = 0;
1538
1539 CHashBundle hb_Loc;
1540
1541 CHashBundle *hb_Use = &hb_Glob;
1542
1543 HRESULT res_SetMethods = S_OK;
1544
1545 UStringVector methods_loc;
1546
1547 if (!hp.Method.IsEmpty())
1548 {
1549 hb_Use = &hb_Loc;
1550 CMethodId id;
1551 if (FindHashMethod(EXTERNAL_CODECS_LOC_VARS hp.Method, id))
1552 {
1553 methods_loc.Add(UString(hp.Method));
1554 RINOK(hb_Loc.SetMethods(
1555 EXTERNAL_CODECS_LOC_VARS
1556 methods_loc));
1557 }
1558 else
1559 res_SetMethods = E_NOTIMPL;
1560 }
1561 else if (methods.IsEmpty())
1562 {
1563 AddDefaultMethod(methods_loc, (unsigned)hp.Hash.Size());
1564 if (!methods_loc.IsEmpty())
1565 {
1566 hb_Use = &hb_Loc;
1567 RINOK(hb_Loc.SetMethods(
1568 EXTERNAL_CODECS_LOC_VARS
1569 methods_loc));
1570 }
1571 }
1572
1573 const bool isSupportedMode = hp.IsSupportedMode();
1574 hb_Use->InitForNewFile();
1575
1576 if (inStream)
1577 {
1578 for (UInt32 step = 0;; step++)
1579 {
1580 if ((step & 0xFF) == 0)
1581 {
1582 RINOK(lps->SetRatioInfo(NULL, &fileSize));
1583 }
1584 UInt32 size;
1585 RINOK(inStream->Read(buf, kBufSize, &size));
1586 if (size == 0)
1587 break;
1588 hb_Use->Update(buf, size);
1589 if (realOutStream)
1590 {
1591 RINOK(WriteStream(realOutStream, buf, size));
1592 }
1593 fileSize += size;
1594 }
1595
1596 hp.Size_from_Disk = fileSize;
1597 hp.Size_from_Disk_Defined = true;
1598 }
1599
1600 realOutStream.Release();
1601 inStream.Release();
1602
1603 lps->InSize += hp.Hash.Size();
1604 lps->OutSize += fileSize;
1605
1606 hb_Use->Final(isDir, isAltStream, path);
1607
1608 Int32 opRes = NArchive::NExtract::NOperationResult::kUnsupportedMethod;
1609 if (isSupportedMode
1610 && res_SetMethods != E_NOTIMPL
1611 && hb_Use->Hashers.Size() > 0
1612 )
1613 {
1614 const CHasherState &hs = hb_Use->Hashers[0];
1615 if (hs.DigestSize == hp.Hash.Size())
1616 {
1617 opRes = NArchive::NExtract::NOperationResult::kCRCError;
1618 if (CheckDigests(hp.Hash, hs.Digests[0], hs.DigestSize))
1619 if (!hp.Size_from_Arc_Defined || hp.Size_from_Arc == fileSize)
1620 opRes = NArchive::NExtract::NOperationResult::kOK;
1621 }
1622 }
1623
1624 RINOK(extractCallback->SetOperationResult(opRes));
1625 }
1626
1627 return lps->SetCur();
1628
1629 COM_TRY_END
1630}
1631
1632
1633// ---------- UPDATE ----------
1634
1635struct CUpdateItem
1636{
1637 int IndexInArc;
1638 unsigned IndexInClient;
1639 UInt64 Size;
1640 bool NewData;
1641 bool NewProps;
1642 bool IsDir;
1643 UString Path;
1644
1645 CUpdateItem(): Size(0), IsDir(false) {}
1646};
1647
1648
1649static HRESULT GetPropString(IArchiveUpdateCallback *callback, UInt32 index, PROPID propId,
1650 UString &res,
1651 bool convertSlash)
1652{
1653 NCOM::CPropVariant prop;
1654 RINOK(callback->GetProperty(index, propId, &prop));
1655 if (prop.vt == VT_BSTR)
1656 {
1657 res = prop.bstrVal;
1658 if (convertSlash)
1659 NArchive::NItemName::ReplaceSlashes_OsToUnix(res);
1660 }
1661 else if (prop.vt != VT_EMPTY)
1662 return E_INVALIDARG;
1663 return S_OK;
1664}
1665
1666
1667STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type)
1668{
1669 *type = NFileTimeType::kUnix;
1670 return S_OK;
1671}
1672
1673
1674STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
1675 IArchiveUpdateCallback *callback)
1676{
1677 COM_TRY_BEGIN
1678
1679 if (_isArc && !CanUpdate())
1680 return E_NOTIMPL;
1681
1682 // const UINT codePage = CP_UTF8; // // (_forceCodePage ? _specifiedCodePage : _openCodePage);
1683 // const unsigned utfFlags = g_Unicode_To_UTF8_Flags;
1684 CObjectVector<CUpdateItem> updateItems;
1685
1686 UInt64 complexity = 0;
1687
1688 UInt32 i;
1689 for (i = 0; i < numItems; i++)
1690 {
1691 CUpdateItem ui;
1692 Int32 newData;
1693 Int32 newProps;
1694 UInt32 indexInArc;
1695
1696 if (!callback)
1697 return E_FAIL;
1698
1699 RINOK(callback->GetUpdateItemInfo(i, &newData, &newProps, &indexInArc));
1700
1701 ui.NewProps = IntToBool(newProps);
1702 ui.NewData = IntToBool(newData);
1703 ui.IndexInArc = (int)indexInArc;
1704 ui.IndexInClient = i;
1705 if (IntToBool(newProps))
1706 {
1707 {
1708 NCOM::CPropVariant prop;
1709 RINOK(callback->GetProperty(i, kpidIsDir, &prop));
1710 if (prop.vt == VT_EMPTY)
1711 ui.IsDir = false;
1712 else if (prop.vt != VT_BOOL)
1713 return E_INVALIDARG;
1714 else
1715 ui.IsDir = (prop.boolVal != VARIANT_FALSE);
1716 }
1717
1718 RINOK(GetPropString(callback, i, kpidPath, ui.Path,
1719 true)); // convertSlash
1720 /*
1721 if (ui.IsDir && !ui.Name.IsEmpty() && ui.Name.Back() != '/')
1722 ui.Name += '/';
1723 */
1724 }
1725
1726 if (IntToBool(newData))
1727 {
1728 NCOM::CPropVariant prop;
1729 RINOK(callback->GetProperty(i, kpidSize, &prop));
1730 if (prop.vt == VT_UI8)
1731 {
1732 ui.Size = prop.uhVal.QuadPart;
1733 complexity += ui.Size;
1734 }
1735 else if (prop.vt == VT_EMPTY)
1736 ui.Size = (UInt64)(Int64)-1;
1737 else
1738 return E_INVALIDARG;
1739 }
1740
1741 updateItems.Add(ui);
1742 }
1743
1744 if (complexity != 0)
1745 {
1746 RINOK(callback->SetTotal(complexity));
1747 }
1748
1749 #ifdef EXTERNAL_CODECS
1750 const CExternalCodecs *__externalCodecs = g_ExternalCodecs_Ptr;
1751 #endif
1752
1753 CHashBundle hb;
1754 UStringVector methods;
1755 if (!_methods.IsEmpty())
1756 {
1757 FOR_VECTOR(k, _methods)
1758 {
1759 methods.Add(_methods[k]);
1760 }
1761 }
1762 else if (_crcSize_WasSet)
1763 {
1764 AddDefaultMethod(methods, _crcSize);
1765 }
1766 else
1767 {
1768 CMyComPtr<IArchiveGetRootProps> getRootProps;
1769 callback->QueryInterface(IID_IArchiveGetRootProps, (void **)&getRootProps);
1770
1771 NCOM::CPropVariant prop;
1772 if (getRootProps)
1773 {
1774 RINOK(getRootProps->GetRootProp(kpidArcFileName, &prop));
1775 if (prop.vt == VT_BSTR)
1776 {
1777 const UString method = GetMethod_from_FileName(prop.bstrVal);
1778 if (!method.IsEmpty())
1779 methods.Add(method);
1780 }
1781 }
1782 }
1783
1784 RINOK(hb.SetMethods(EXTERNAL_CODECS_LOC_VARS methods));
1785
1786 CLocalProgress *lps = new CLocalProgress;
1787 CMyComPtr<ICompressProgressInfo> progress = lps;
1788 lps->Init(callback, true);
1789
1790 const UInt32 kBufSize = 1 << 15;
1791 CHashMidBuf buf;
1792 if (!buf.Alloc(kBufSize))
1793 return E_OUTOFMEMORY;
1794
1795 CDynLimBuf hashFileString((size_t)1 << 31);
1796
1797 CHashOptionsLocal options = _options;
1798
1799 if (_isArc)
1800 {
1801 if (!options.HashMode_Zero.Def && _is_ZeroMode)
1802 options.HashMode_Zero.Val = true;
1803 if (!options.HashMode_Tag.Def && _are_there_Tags)
1804 options.HashMode_Tag.Val = true;
1805 if (!options.HashMode_Dirs.Def && _are_there_Dirs)
1806 options.HashMode_Dirs.Val = true;
1807 }
1808 if (options.HashMode_OnlyHash.Val && updateItems.Size() != 1)
1809 options.HashMode_OnlyHash.Val = false;
1810
1811 lps->OutSize = 0;
1812 complexity = 0;
1813
1814 for (i = 0; i < updateItems.Size(); i++)
1815 {
1816 lps->InSize = complexity;
1817 RINOK(lps->SetCur());
1818
1819 const CUpdateItem &ui = updateItems[i];
1820
1821 /*
1822 CHashPair item;
1823 if (!ui.NewProps)
1824 item = HashPairs[(unsigned)ui.IndexInArc];
1825 */
1826
1827 if (ui.NewData)
1828 {
1829 UInt64 currentComplexity = ui.Size;
1830 CMyComPtr<ISequentialInStream> fileInStream;
1831 bool needWrite = true;
1832 {
1833 HRESULT res = callback->GetStream(ui.IndexInClient, &fileInStream);
1834
1835 if (res == S_FALSE)
1836 needWrite = false;
1837 else
1838 {
1839 RINOK(res);
1840
1841 if (fileInStream)
1842 {
1843 CMyComPtr<IStreamGetProps> getProps;
1844 fileInStream->QueryInterface(IID_IStreamGetProps, (void **)&getProps);
1845 if (getProps)
1846 {
1847 FILETIME mTime;
1848 UInt64 size2;
1849 if (getProps->GetProps(&size2, NULL, NULL, &mTime, NULL) == S_OK)
1850 {
1851 currentComplexity = size2;
1852 // item.MTime = NWindows::NTime::FileTimeToUnixTime64(mTime);;
1853 }
1854 }
1855 }
1856 else
1857 {
1858 currentComplexity = 0;
1859 }
1860 }
1861 }
1862
1863 hb.InitForNewFile();
1864 const bool isDir = ui.IsDir;
1865
1866 if (needWrite && fileInStream && !isDir)
1867 {
1868 UInt64 fileSize = 0;
1869 for (UInt32 step = 0;; step++)
1870 {
1871 if ((step & 0xFF) == 0)
1872 {
1873 RINOK(lps->SetRatioInfo(&fileSize, NULL));
1874 // RINOK(callback->SetCompleted(&completeValue));
1875 }
1876 UInt32 size;
1877 RINOK(fileInStream->Read(buf, kBufSize, &size));
1878 if (size == 0)
1879 break;
1880 hb.Update(buf, size);
1881 fileSize += size;
1882 }
1883 currentComplexity = fileSize;
1884 }
1885
1886 fileInStream.Release();
1887 const bool isAltStream = false;
1888 hb.Final(isDir, isAltStream, ui.Path);
1889
1890 if (options.HashMode_Dirs.Val || !isDir)
1891 {
1892 if (!hb.Hashers.IsEmpty())
1893 lps->OutSize += hb.Hashers[0].DigestSize;
1894 WriteLine(hashFileString,
1895 options,
1896 ui.Path,
1897 isDir,
1898 hb);
1899 if (hashFileString.IsError())
1900 return E_OUTOFMEMORY;
1901 }
1902
1903 complexity += currentComplexity;
1904 RINOK(callback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK));
1905 }
1906 else
1907 {
1908 // old data
1909 const CHashPair &existItem = HashPairs[(unsigned)ui.IndexInArc];
1910 if (ui.NewProps)
1911 {
1912 WriteLine(hashFileString,
1913 options,
1914 ui.Path,
1915 ui.IsDir,
1916 existItem.Method, existItem.HashString
1917 );
1918 }
1919 else
1920 {
1921 hashFileString += existItem.FullLine;
1922 Add_LF(hashFileString, options);
1923 }
1924 }
1925 if (hashFileString.IsError())
1926 return E_OUTOFMEMORY;
1927 }
1928
1929 RINOK(WriteStream(outStream, hashFileString, hashFileString.Len()));
1930
1931 return S_OK;
1932 COM_TRY_END
1933}
1934
1935
1936
1937HRESULT CHandler::SetProperty(const wchar_t *nameSpec, const PROPVARIANT &value)
1938{
1939 UString name = nameSpec;
1940 name.MakeLower_Ascii();
1941 if (name.IsEmpty())
1942 return E_INVALIDARG;
1943
1944 if (name.IsEqualTo("m")) // "hm" hash method
1945 {
1946 // COneMethodInfo omi;
1947 // RINOK(omi.ParseMethodFromPROPVARIANT(L"", value));
1948 // _methods.Add(omi.MethodName); // change it. use omi.PropsString
1949 if (value.vt != VT_BSTR)
1950 return E_INVALIDARG;
1951 UString s (value.bstrVal);
1952 _methods.Add(s);
1953 return S_OK;
1954 }
1955
1956 if (name.IsEqualTo("flags"))
1957 {
1958 if (value.vt != VT_BSTR)
1959 return E_INVALIDARG;
1960 if (!_options.ParseString(value.bstrVal))
1961 return E_INVALIDARG;
1962 return S_OK;
1963 }
1964
1965 if (name.IsPrefixedBy_Ascii_NoCase("crc"))
1966 {
1967 name.Delete(0, 3);
1968 _crcSize = 4;
1969 _crcSize_WasSet = true;
1970 return ParsePropToUInt32(name, value, _crcSize);
1971 }
1972
1973 // common properties
1974 if (name.IsPrefixedBy_Ascii_NoCase("mt")
1975 || name.IsPrefixedBy_Ascii_NoCase("memuse"))
1976 return S_OK;
1977
1978 return E_INVALIDARG;
1979}
1980
1981
1982STDMETHODIMP CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps)
1983{
1984 COM_TRY_BEGIN
1985
1986 InitProps();
1987
1988 for (UInt32 i = 0; i < numProps; i++)
1989 {
1990 RINOK(SetProperty(names[i], values[i]));
1991 }
1992 return S_OK;
1993 COM_TRY_END
1994}
1995
1996CHandler::CHandler()
1997{
1998 ClearVars();
1999 InitProps();
2000}
2001
2002}
2003
2004
2005
2006static IInArchive *CreateHashHandler_In() { return new NHash::CHandler; }
2007static IOutArchive *CreateHashHandler_Out() { return new NHash::CHandler; }
2008
2009void Codecs_AddHashArcHandler(CCodecs *codecs)
2010{
2011 {
2012 CArcInfoEx item;
2013
2014 item.Name = "Hash";
2015 item.CreateInArchive = CreateHashHandler_In;
2016 item.CreateOutArchive = CreateHashHandler_Out;
2017 item.IsArcFunc = NULL;
2018 item.Flags =
2019 NArcInfoFlags::kKeepName
2020 | NArcInfoFlags::kStartOpen
2021 | NArcInfoFlags::kByExtOnlyOpen
2022 // | NArcInfoFlags::kPureStartOpen
2023 | NArcInfoFlags::kHashHandler
2024 ;
2025
2026 // ubuntu uses "SHA256SUMS" file
2027 item.AddExts(UString (
2028 "sha256 sha512 sha224 sha384 sha1 sha md5"
2029 // "b2sum"
2030 " crc32 crc64"
2031 " asc"
2032 " cksum"
2033 ),
2034 UString());
2035
2036 item.UpdateEnabled = (item.CreateOutArchive != NULL);
2037 item.SignatureOffset = 0;
2038 // item.Version = MY_VER_MIX;
2039 item.NewInterface = true;
2040
2041 item.Signatures.AddNew().CopyFrom(NULL, 0);
2042
2043 codecs->Formats.Add(item);
2044 }
2045}
diff --git a/CPP/7zip/UI/Common/HashCalc.h b/CPP/7zip/UI/Common/HashCalc.h
new file mode 100644
index 0000000..80a5565
--- /dev/null
+++ b/CPP/7zip/UI/Common/HashCalc.h
@@ -0,0 +1,334 @@
1// HashCalc.h
2
3#ifndef __HASH_CALC_H
4#define __HASH_CALC_H
5
6#include "../../../Common/UTFConvert.h"
7#include "../../../Common/Wildcard.h"
8
9#include "../../Common/CreateCoder.h"
10#include "../../Common/MethodProps.h"
11
12#include "DirItem.h"
13#include "IFileExtractCallback.h"
14
15const unsigned k_HashCalc_DigestSize_Max = 64;
16const unsigned k_HashCalc_ExtraSize = 8;
17const unsigned k_HashCalc_NumGroups = 4;
18
19enum
20{
21 k_HashCalc_Index_Current,
22 k_HashCalc_Index_DataSum,
23 k_HashCalc_Index_NamesSum,
24 k_HashCalc_Index_StreamsSum
25};
26
27struct CHasherState
28{
29 CMyComPtr<IHasher> Hasher;
30 AString Name;
31 UInt32 DigestSize;
32 UInt64 NumSums[k_HashCalc_NumGroups];
33 Byte Digests[k_HashCalc_NumGroups][k_HashCalc_DigestSize_Max + k_HashCalc_ExtraSize];
34
35 void InitDigestGroup(unsigned groupIndex)
36 {
37 NumSums[groupIndex] = 0;
38 memset(Digests[groupIndex], 0, sizeof(Digests[groupIndex]));
39 }
40
41 const Byte *GetExtraData_for_Group(unsigned groupIndex) const
42 {
43 return Digests[groupIndex] + k_HashCalc_DigestSize_Max;
44 }
45
46 unsigned GetNumExtraBytes_for_Group(unsigned groupIndex) const
47 {
48 const Byte *p = GetExtraData_for_Group(groupIndex);
49 // we use little-endian to read extra bytes
50 for (unsigned i = k_HashCalc_ExtraSize; i != 0; i--)
51 if (p[i - 1] != 0)
52 return i;
53 return 0;
54 }
55
56 void AddDigest(unsigned groupIndex, const Byte *data);
57
58 void WriteToString(unsigned digestIndex, char *s) const;
59};
60
61
62
63struct IHashCalc
64{
65 virtual void InitForNewFile() = 0;
66 virtual void Update(const void *data, UInt32 size) = 0;
67 virtual void SetSize(UInt64 size) = 0;
68 virtual void Final(bool isDir, bool isAltStream, const UString &path) = 0;
69};
70
71struct CHashBundle: public IHashCalc
72{
73 CObjectVector<CHasherState> Hashers;
74
75 UInt64 NumDirs;
76 UInt64 NumFiles;
77 UInt64 NumAltStreams;
78 UInt64 FilesSize;
79 UInt64 AltStreamsSize;
80 UInt64 NumErrors;
81
82 UInt64 CurSize;
83
84 UString MainName;
85 UString FirstFileName;
86
87 HRESULT SetMethods(DECL_EXTERNAL_CODECS_LOC_VARS const UStringVector &methods);
88
89 // void Init() {}
90 CHashBundle()
91 {
92 NumDirs = NumFiles = NumAltStreams = FilesSize = AltStreamsSize = NumErrors = 0;
93 }
94
95 virtual ~CHashBundle() {};
96
97 void InitForNewFile();
98 void Update(const void *data, UInt32 size);
99 void SetSize(UInt64 size);
100 void Final(bool isDir, bool isAltStream, const UString &path);
101};
102
103#define INTERFACE_IHashCallbackUI(x) \
104 INTERFACE_IDirItemsCallback(x) \
105 virtual HRESULT StartScanning() x; \
106 virtual HRESULT FinishScanning(const CDirItemsStat &st) x; \
107 virtual HRESULT SetNumFiles(UInt64 numFiles) x; \
108 virtual HRESULT SetTotal(UInt64 size) x; \
109 virtual HRESULT SetCompleted(const UInt64 *completeValue) x; \
110 virtual HRESULT CheckBreak() x; \
111 virtual HRESULT BeforeFirstFile(const CHashBundle &hb) x; \
112 virtual HRESULT GetStream(const wchar_t *name, bool isFolder) x; \
113 virtual HRESULT OpenFileError(const FString &path, DWORD systemError) x; \
114 virtual HRESULT SetOperationResult(UInt64 fileSize, const CHashBundle &hb, bool showHash) x; \
115 virtual HRESULT AfterLastFile(CHashBundle &hb) x; \
116
117struct IHashCallbackUI: public IDirItemsCallback
118{
119 INTERFACE_IHashCallbackUI(=0)
120};
121
122
123struct CHashOptionsLocal
124{
125 CBoolPair HashMode_Zero;
126 CBoolPair HashMode_Tag;
127 CBoolPair HashMode_Dirs;
128 CBoolPair HashMode_OnlyHash;
129
130 void Init_HashOptionsLocal()
131 {
132 HashMode_Zero.Init();
133 HashMode_Tag.Init();
134 HashMode_Dirs.Init();
135 HashMode_OnlyHash.Init();
136 // HashMode_Dirs = true; // for debug
137 }
138
139 CHashOptionsLocal()
140 {
141 Init_HashOptionsLocal();
142 }
143
144 bool ParseFlagCharOption(wchar_t c, bool val)
145 {
146 c = MyCharLower_Ascii(c);
147 if (c == 'z') HashMode_Zero.SetVal_as_Defined(val);
148 else if (c == 't') HashMode_Tag.SetVal_as_Defined(val);
149 else if (c == 'd') HashMode_Dirs.SetVal_as_Defined(val);
150 else if (c == 'h') HashMode_OnlyHash.SetVal_as_Defined(val);
151 else return false;
152 return true;
153 }
154
155 bool ParseString(const UString &s)
156 {
157 for (unsigned i = 0; i < s.Len();)
158 {
159 const wchar_t c = s[i++];
160 bool val = true;
161 if (i < s.Len())
162 {
163 const wchar_t next = s[i];
164 if (next == '-')
165 {
166 val = false;
167 i++;
168 }
169 }
170 if (!ParseFlagCharOption(c, val))
171 return false;
172 }
173 return true;
174 }
175};
176
177
178struct CHashOptions
179 // : public CHashOptionsLocal
180{
181 UStringVector Methods;
182 // UString HashFilePath;
183
184 bool PreserveATime;
185 bool OpenShareForWrite;
186 bool StdInMode;
187 bool AltStreamsMode;
188 CBoolPair SymLinks;
189
190 NWildcard::ECensorPathMode PathMode;
191
192 CHashOptions():
193 PreserveATime(false),
194 OpenShareForWrite(false),
195 StdInMode(false),
196 AltStreamsMode(false),
197 PathMode(NWildcard::k_RelatPath) {};
198};
199
200
201HRESULT HashCalc(
202 DECL_EXTERNAL_CODECS_LOC_VARS
203 const NWildcard::CCensor &censor,
204 const CHashOptions &options,
205 AString &errorInfo,
206 IHashCallbackUI *callback);
207
208
209
210#ifndef _SFX
211
212namespace NHash {
213
214struct CHashPair
215{
216 CByteBuffer Hash;
217 char Mode;
218 bool IsBSD;
219 bool Size_from_Arc_Defined;
220 bool Size_from_Disk_Defined;
221 AString Method;
222 AString Name;
223
224 AString FullLine;
225 AString HashString;
226 // unsigned HashLengthInBits;
227
228 // AString MethodName;
229 UInt64 Size_from_Arc;
230 UInt64 Size_from_Disk;
231
232 bool IsDir() const;
233
234 void Get_UString_Path(UString &path) const
235 {
236 path.Empty();
237 if (!ConvertUTF8ToUnicode(Name, path))
238 return;
239 }
240
241 bool ParseCksum(const char *s);
242 bool Parse(const char *s);
243
244 bool IsSupportedMode() const
245 {
246 return Mode != 'U' && Mode != '^';
247 }
248
249 CHashPair():
250 Mode(0)
251 , IsBSD(false)
252 , Size_from_Arc_Defined(false)
253 , Size_from_Disk_Defined(false)
254 // , HashLengthInBits(0)
255 , Size_from_Arc(0)
256 , Size_from_Disk(0)
257 {}
258};
259
260
261class CHandler:
262 public IInArchive,
263 public IArchiveGetRawProps,
264 // public IGetArchiveHashHandler,
265 public IOutArchive,
266 public ISetProperties,
267 public CMyUnknownImp
268{
269 bool _isArc;
270 UInt64 _phySize;
271 CObjectVector<CHashPair> HashPairs;
272 UString _nameExtenstion;
273 // UString _method_fromName;
274 AString _pgpMethod;
275 bool _is_CksumMode;
276 bool _is_PgpMethod;
277 bool _is_ZeroMode;
278 bool _are_there_Tags;
279 bool _are_there_Dirs;
280 bool _hashSize_Defined;
281 unsigned _hashSize;
282
283 bool _crcSize_WasSet;
284 UInt32 _crcSize;
285 UStringVector _methods;
286
287 void ClearVars();
288
289 void InitProps()
290 {
291 _crcSize_WasSet = false;
292 _crcSize = 4;
293 _methods.Clear();
294 _options.Init_HashOptionsLocal();
295 }
296
297 CHashOptionsLocal _options;
298
299 bool CanUpdate() const
300 {
301 if (!_isArc || _is_PgpMethod || _is_CksumMode)
302 return false;
303 return true;
304
305 }
306
307 HRESULT SetProperty(const wchar_t *nameSpec, const PROPVARIANT &value);
308
309public:
310
311 CHandler();
312
313 MY_UNKNOWN_IMP4(
314 IInArchive,
315 IArchiveGetRawProps,
316 IOutArchive,
317 ISetProperties
318 /*, IGetArchiveHashHandler */
319 )
320 INTERFACE_IInArchive(;)
321 INTERFACE_IOutArchive(;)
322 INTERFACE_IArchiveGetRawProps(;)
323 // STDMETHOD(GetArchiveHashHandler)(CHandler **handler);
324 STDMETHOD(SetProperties)(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps);
325};
326
327}
328
329void Codecs_AddHashArcHandler(CCodecs *codecs);
330
331#endif
332
333
334#endif
diff --git a/CPP/7zip/UI/Common/IFileExtractCallback.h b/CPP/7zip/UI/Common/IFileExtractCallback.h
new file mode 100644
index 0000000..e6a85c6
--- /dev/null
+++ b/CPP/7zip/UI/Common/IFileExtractCallback.h
@@ -0,0 +1,114 @@
1// IFileExtractCallback.h
2
3#ifndef __I_FILE_EXTRACT_CALLBACK_H
4#define __I_FILE_EXTRACT_CALLBACK_H
5
6#include "../../../Common/MyString.h"
7
8#include "../../IDecl.h"
9
10#include "LoadCodecs.h"
11#include "OpenArchive.h"
12
13namespace NOverwriteAnswer
14{
15 enum EEnum
16 {
17 kYes,
18 kYesToAll,
19 kNo,
20 kNoToAll,
21 kAutoRename,
22 kCancel
23 };
24}
25
26
27/* ---------- IFolderArchiveExtractCallback ----------
28is implemented by
29 Console/ExtractCallbackConsole.h CExtractCallbackConsole
30 FileManager/ExtractCallback.h CExtractCallbackImp
31 FAR/ExtractEngine.cpp CExtractCallBackImp: (QueryInterface is not supported)
32
33IID_IFolderArchiveExtractCallback is requested by:
34 - Agent/ArchiveFolder.cpp
35 CAgentFolder::CopyTo(..., IFolderOperationsExtractCallback *callback)
36 is sent to IArchiveFolder::Extract()
37
38 - FileManager/PanelCopy.cpp
39 CPanel::CopyTo(), if (options->testMode)
40 is sent to IArchiveFolder::Extract()
41
42 IFolderArchiveExtractCallback is used by Common/ArchiveExtractCallback.cpp
43*/
44
45#define INTERFACE_IFolderArchiveExtractCallback(x) \
46 STDMETHOD(AskOverwrite)( \
47 const wchar_t *existName, const FILETIME *existTime, const UInt64 *existSize, \
48 const wchar_t *newName, const FILETIME *newTime, const UInt64 *newSize, \
49 Int32 *answer) x; \
50 STDMETHOD(PrepareOperation)(const wchar_t *name, Int32 isFolder, Int32 askExtractMode, const UInt64 *position) x; \
51 STDMETHOD(MessageError)(const wchar_t *message) x; \
52 STDMETHOD(SetOperationResult)(Int32 opRes, Int32 encrypted) x; \
53
54DECL_INTERFACE_SUB(IFolderArchiveExtractCallback, IProgress, 0x01, 0x07)
55{
56 INTERFACE_IFolderArchiveExtractCallback(PURE)
57};
58
59#define INTERFACE_IFolderArchiveExtractCallback2(x) \
60 STDMETHOD(ReportExtractResult)(Int32 opRes, Int32 encrypted, const wchar_t *name) x; \
61
62DECL_INTERFACE_SUB(IFolderArchiveExtractCallback2, IUnknown, 0x01, 0x08)
63{
64 INTERFACE_IFolderArchiveExtractCallback2(PURE)
65};
66
67/* ---------- IExtractCallbackUI ----------
68is implemented by
69 Console/ExtractCallbackConsole.h CExtractCallbackConsole
70 FileManager/ExtractCallback.h CExtractCallbackImp
71*/
72
73#ifdef _NO_CRYPTO
74 #define INTERFACE_IExtractCallbackUI_Crypto(x)
75#else
76 #define INTERFACE_IExtractCallbackUI_Crypto(x) \
77 virtual HRESULT SetPassword(const UString &password) x;
78#endif
79
80#define INTERFACE_IExtractCallbackUI(x) \
81 virtual HRESULT BeforeOpen(const wchar_t *name, bool testMode) x; \
82 virtual HRESULT OpenResult(const CCodecs *codecs, const CArchiveLink &arcLink, const wchar_t *name, HRESULT result) x; \
83 virtual HRESULT ThereAreNoFiles() x; \
84 virtual HRESULT ExtractResult(HRESULT result) x; \
85 INTERFACE_IExtractCallbackUI_Crypto(x)
86
87struct IExtractCallbackUI: IFolderArchiveExtractCallback
88{
89 INTERFACE_IExtractCallbackUI(PURE)
90};
91
92
93
94#define INTERFACE_IGetProp(x) \
95 STDMETHOD(GetProp)(PROPID propID, PROPVARIANT *value) x; \
96
97DECL_INTERFACE_SUB(IGetProp, IUnknown, 0x01, 0x20)
98{
99 INTERFACE_IGetProp(PURE)
100};
101
102#define INTERFACE_IFolderExtractToStreamCallback(x) \
103 STDMETHOD(UseExtractToStream)(Int32 *res) x; \
104 STDMETHOD(GetStream7)(const wchar_t *name, Int32 isDir, ISequentialOutStream **outStream, Int32 askExtractMode, IGetProp *getProp) x; \
105 STDMETHOD(PrepareOperation7)(Int32 askExtractMode) x; \
106 STDMETHOD(SetOperationResult8)(Int32 resultEOperationResult, Int32 encrypted, UInt64 size) x; \
107
108DECL_INTERFACE_SUB(IFolderExtractToStreamCallback, IUnknown, 0x01, 0x31)
109{
110 INTERFACE_IFolderExtractToStreamCallback(PURE)
111};
112
113
114#endif
diff --git a/CPP/7zip/UI/Common/LoadCodecs.cpp b/CPP/7zip/UI/Common/LoadCodecs.cpp
new file mode 100644
index 0000000..377963a
--- /dev/null
+++ b/CPP/7zip/UI/Common/LoadCodecs.cpp
@@ -0,0 +1,1321 @@
1// LoadCodecs.cpp
2
3/*
4EXTERNAL_CODECS
5---------------
6 CCodecs::Load() tries to detect the directory with plugins.
7 It stops the checking, if it can find any of the following items:
8 - 7z.dll file
9 - "Formats" subdir
10 - "Codecs" subdir
11 The order of check:
12 1) directory of client executable
13 2) WIN32: directory for REGISTRY item [HKEY_*\Software\7-Zip\Path**]
14 The order for HKEY_* : Path** :
15 - HKEY_CURRENT_USER : PathXX
16 - HKEY_LOCAL_MACHINE : PathXX
17 - HKEY_CURRENT_USER : Path
18 - HKEY_LOCAL_MACHINE : Path
19 PathXX is Path32 in 32-bit code
20 PathXX is Path64 in 64-bit code
21
22
23EXPORT_CODECS
24-------------
25 if (EXTERNAL_CODECS) is defined, then the code exports internal
26 codecs of client from CCodecs object to external plugins.
27 7-Zip doesn't use that feature. 7-Zip uses the scheme:
28 - client application without internal plugins.
29 - 7z.dll module contains all (or almost all) plugins.
30 7z.dll can use codecs from another plugins, if required.
31*/
32
33
34#include "StdAfx.h"
35
36#include "../../../../C/7zVersion.h"
37
38#include "../../../Common/MyCom.h"
39#include "../../../Common/StringToInt.h"
40#include "../../../Common/StringConvert.h"
41
42#include "../../../Windows/ErrorMsg.h"
43#include "../../../Windows/FileIO.h"
44#include "../../../Windows/PropVariant.h"
45
46#include "LoadCodecs.h"
47
48using namespace NWindows;
49
50#ifdef NEW_FOLDER_INTERFACE
51#include "../../../Common/StringToInt.h"
52#endif
53
54#include "../../ICoder.h"
55#include "../../Common/RegisterArc.h"
56#include "../../Common/RegisterCodec.h"
57
58#ifdef EXTERNAL_CODECS
59
60// #define EXPORT_CODECS
61
62#endif
63
64#ifdef NEW_FOLDER_INTERFACE
65extern HINSTANCE g_hInstance;
66#include "../../../Windows/ResourceString.h"
67static const UINT kIconTypesResId = 100;
68#endif
69
70#ifdef EXTERNAL_CODECS
71
72#include "../../../Windows/FileFind.h"
73#include "../../../Windows/DLL.h"
74
75#ifdef _WIN32
76#include "../../../Windows/FileName.h"
77#include "../../../Windows/Registry.h"
78#endif
79
80using namespace NFile;
81
82
83#define kCodecsFolderName FTEXT("Codecs")
84#define kFormatsFolderName FTEXT("Formats")
85
86
87static CFSTR const kMainDll =
88 #ifdef _WIN32
89 FTEXT("7z.dll");
90 #else
91 FTEXT("7z.so");
92 #endif
93
94
95#ifdef _WIN32
96
97static LPCTSTR const kRegistryPath = TEXT("Software") TEXT(STRING_PATH_SEPARATOR) TEXT("7-zip");
98static LPCWSTR const kProgramPathValue = L"Path";
99static LPCWSTR const kProgramPath2Value = L"Path"
100 #ifdef _WIN64
101 L"64";
102 #else
103 L"32";
104 #endif
105
106static bool ReadPathFromRegistry(HKEY baseKey, LPCWSTR value, FString &path)
107{
108 NRegistry::CKey key;
109 if (key.Open(baseKey, kRegistryPath, KEY_READ) == ERROR_SUCCESS)
110 {
111 UString pathU;
112 if (key.QueryValue(value, pathU) == ERROR_SUCCESS)
113 {
114 path = us2fs(pathU);
115 NName::NormalizeDirPathPrefix(path);
116 return NFind::DoesFileExist_Raw(path + kMainDll);
117 }
118 }
119 return false;
120}
121
122#endif // _WIN32
123
124#endif // EXTERNAL_CODECS
125
126
127static const unsigned kNumArcsMax = 64;
128static unsigned g_NumArcs = 0;
129static const CArcInfo *g_Arcs[kNumArcsMax];
130
131void RegisterArc(const CArcInfo *arcInfo) throw()
132{
133 if (g_NumArcs < kNumArcsMax)
134 {
135 g_Arcs[g_NumArcs] = arcInfo;
136 g_NumArcs++;
137 }
138}
139
140static void SplitString(const UString &srcString, UStringVector &destStrings)
141{
142 destStrings.Clear();
143 UString s;
144 unsigned len = srcString.Len();
145 if (len == 0)
146 return;
147 for (unsigned i = 0; i < len; i++)
148 {
149 wchar_t c = srcString[i];
150 if (c == L' ')
151 {
152 if (!s.IsEmpty())
153 {
154 destStrings.Add(s);
155 s.Empty();
156 }
157 }
158 else
159 s += c;
160 }
161 if (!s.IsEmpty())
162 destStrings.Add(s);
163}
164
165int CArcInfoEx::FindExtension(const UString &ext) const
166{
167 FOR_VECTOR (i, Exts)
168 if (ext.IsEqualTo_NoCase(Exts[i].Ext))
169 return (int)i;
170 return -1;
171}
172
173void CArcInfoEx::AddExts(const UString &ext, const UString &addExt)
174{
175 UStringVector exts, addExts;
176 SplitString(ext, exts);
177 SplitString(addExt, addExts);
178 FOR_VECTOR (i, exts)
179 {
180 CArcExtInfo extInfo;
181 extInfo.Ext = exts[i];
182 if (i < addExts.Size())
183 {
184 extInfo.AddExt = addExts[i];
185 if (extInfo.AddExt == L"*")
186 extInfo.AddExt.Empty();
187 }
188 Exts.Add(extInfo);
189 }
190}
191
192#ifndef _SFX
193
194static bool ParseSignatures(const Byte *data, unsigned size, CObjectVector<CByteBuffer> &signatures)
195{
196 signatures.Clear();
197 while (size != 0)
198 {
199 const unsigned len = *data++;
200 size--;
201 if (len > size)
202 return false;
203 signatures.AddNew().CopyFrom(data, len);
204 data += len;
205 size -= len;
206 }
207 return true;
208}
209
210#endif // _SFX
211
212// #include <stdio.h>
213
214#ifdef EXTERNAL_CODECS
215
216static FString GetBaseFolderPrefixFromRegistry()
217{
218 FString moduleFolderPrefix = NDLL::GetModuleDirPrefix();
219
220 #ifdef _WIN32
221 if ( !NFind::DoesFileOrDirExist(moduleFolderPrefix + kMainDll)
222 && !NFind::DoesFileOrDirExist(moduleFolderPrefix + kCodecsFolderName)
223 && !NFind::DoesFileOrDirExist(moduleFolderPrefix + kFormatsFolderName))
224 {
225 FString path;
226 if (ReadPathFromRegistry(HKEY_CURRENT_USER, kProgramPath2Value, path)) return path;
227 if (ReadPathFromRegistry(HKEY_LOCAL_MACHINE, kProgramPath2Value, path)) return path;
228 if (ReadPathFromRegistry(HKEY_CURRENT_USER, kProgramPathValue, path)) return path;
229 if (ReadPathFromRegistry(HKEY_LOCAL_MACHINE, kProgramPathValue, path)) return path;
230 }
231 #endif
232
233 // printf("\nmoduleFolderPrefix = %s\n", (const char *)GetAnsiString(moduleFolderPrefix));
234 return moduleFolderPrefix;
235}
236
237
238static HRESULT GetCoderClass(Func_GetMethodProperty getMethodProperty, UInt32 index,
239 PROPID propId, CLSID &clsId, bool &isAssigned)
240{
241 NCOM::CPropVariant prop;
242 isAssigned = false;
243 RINOK(getMethodProperty(index, propId, &prop));
244 if (prop.vt == VT_BSTR)
245 {
246 if (::SysStringByteLen(prop.bstrVal) != sizeof(GUID))
247 return E_FAIL;
248 isAssigned = true;
249 clsId = *(const GUID *)(const void *)prop.bstrVal;
250 }
251 else if (prop.vt != VT_EMPTY)
252 return E_FAIL;
253 return S_OK;
254}
255
256
257static HRESULT GetMethodBoolProp(Func_GetMethodProperty getMethodProperty, UInt32 index,
258 PROPID propId, bool &resVal, bool &isAssigned)
259{
260 NCOM::CPropVariant prop;
261 resVal = false;
262 isAssigned = false;
263 RINOK(getMethodProperty(index, propId, &prop));
264 if (prop.vt == VT_BOOL)
265 {
266 isAssigned = true;
267 resVal = VARIANT_BOOLToBool(prop.boolVal);
268 }
269 else if (prop.vt != VT_EMPTY)
270 return E_FAIL;
271 return S_OK;
272}
273
274
275#define MY_GET_FUNC(dest, type, func) *(void **)(&dest) = (func);
276// #define MY_GET_FUNC(dest, type, func) dest = (type)(func);
277
278HRESULT CCodecs::LoadCodecs()
279{
280 CCodecLib &lib = Libs.Back();
281
282 MY_GET_FUNC (lib.CreateDecoder, Func_CreateDecoder, lib.Lib.GetProc("CreateDecoder"));
283 MY_GET_FUNC (lib.CreateEncoder, Func_CreateEncoder, lib.Lib.GetProc("CreateEncoder"));
284 MY_GET_FUNC (lib.GetMethodProperty, Func_GetMethodProperty, lib.Lib.GetProc("GetMethodProperty"));
285
286 if (lib.GetMethodProperty)
287 {
288 UInt32 numMethods = 1;
289 Func_GetNumberOfMethods getNumberOfMethods;
290 MY_GET_FUNC (getNumberOfMethods, Func_GetNumberOfMethods, lib.Lib.GetProc("GetNumberOfMethods"));
291 if (getNumberOfMethods)
292 {
293 RINOK(getNumberOfMethods(&numMethods));
294 }
295 for (UInt32 i = 0; i < numMethods; i++)
296 {
297 CDllCodecInfo info;
298 info.LibIndex = Libs.Size() - 1;
299 info.CodecIndex = i;
300 RINOK(GetCoderClass(lib.GetMethodProperty, i, NMethodPropID::kEncoder, info.Encoder, info.EncoderIsAssigned));
301 RINOK(GetCoderClass(lib.GetMethodProperty, i, NMethodPropID::kDecoder, info.Decoder, info.DecoderIsAssigned));
302 RINOK(GetMethodBoolProp(lib.GetMethodProperty, i, NMethodPropID::kIsFilter, info.IsFilter, info.IsFilter_Assigned));
303 Codecs.Add(info);
304 }
305 }
306
307 Func_GetHashers getHashers;
308 MY_GET_FUNC (getHashers, Func_GetHashers, lib.Lib.GetProc("GetHashers"));
309 if (getHashers)
310 {
311 RINOK(getHashers(&lib.ComHashers));
312 if (lib.ComHashers)
313 {
314 UInt32 numMethods = lib.ComHashers->GetNumHashers();
315 for (UInt32 i = 0; i < numMethods; i++)
316 {
317 CDllHasherInfo info;
318 info.LibIndex = Libs.Size() - 1;
319 info.HasherIndex = i;
320 Hashers.Add(info);
321 }
322 }
323 }
324
325 return S_OK;
326}
327
328static HRESULT GetProp(
329 Func_GetHandlerProperty getProp,
330 Func_GetHandlerProperty2 getProp2,
331 UInt32 index, PROPID propID, NCOM::CPropVariant &prop)
332{
333 if (getProp2)
334 return getProp2(index, propID, &prop);;
335 return getProp(propID, &prop);
336}
337
338static HRESULT GetProp_Bool(
339 Func_GetHandlerProperty getProp,
340 Func_GetHandlerProperty2 getProp2,
341 UInt32 index, PROPID propID, bool &res)
342{
343 res = false;
344 NCOM::CPropVariant prop;
345 RINOK(GetProp(getProp, getProp2, index, propID, prop));
346 if (prop.vt == VT_BOOL)
347 res = VARIANT_BOOLToBool(prop.boolVal);
348 else if (prop.vt != VT_EMPTY)
349 return E_FAIL;
350 return S_OK;
351}
352
353static HRESULT GetProp_UInt32(
354 Func_GetHandlerProperty getProp,
355 Func_GetHandlerProperty2 getProp2,
356 UInt32 index, PROPID propID, UInt32 &res, bool &defined)
357{
358 res = 0;
359 defined = false;
360 NCOM::CPropVariant prop;
361 RINOK(GetProp(getProp, getProp2, index, propID, prop));
362 if (prop.vt == VT_UI4)
363 {
364 res = prop.ulVal;
365 defined = true;
366 }
367 else if (prop.vt != VT_EMPTY)
368 return E_FAIL;
369 return S_OK;
370}
371
372static HRESULT GetProp_String(
373 Func_GetHandlerProperty getProp,
374 Func_GetHandlerProperty2 getProp2,
375 UInt32 index, PROPID propID, UString &res)
376{
377 res.Empty();
378 NCOM::CPropVariant prop;
379 RINOK(GetProp(getProp, getProp2, index, propID, prop));
380 if (prop.vt == VT_BSTR)
381 res.SetFromBstr(prop.bstrVal);
382 else if (prop.vt != VT_EMPTY)
383 return E_FAIL;
384 return S_OK;
385}
386
387static HRESULT GetProp_RawData(
388 Func_GetHandlerProperty getProp,
389 Func_GetHandlerProperty2 getProp2,
390 UInt32 index, PROPID propID, CByteBuffer &bb)
391{
392 bb.Free();
393 NCOM::CPropVariant prop;
394 RINOK(GetProp(getProp, getProp2, index, propID, prop));
395 if (prop.vt == VT_BSTR)
396 {
397 UINT len = ::SysStringByteLen(prop.bstrVal);
398 bb.CopyFrom((const Byte *)prop.bstrVal, len);
399 }
400 else if (prop.vt != VT_EMPTY)
401 return E_FAIL;
402 return S_OK;
403}
404
405static const UInt32 kArcFlagsPars[] =
406{
407 NArchive::NHandlerPropID::kKeepName, NArcInfoFlags::kKeepName,
408 NArchive::NHandlerPropID::kAltStreams, NArcInfoFlags::kAltStreams,
409 NArchive::NHandlerPropID::kNtSecure, NArcInfoFlags::kNtSecure
410};
411
412HRESULT CCodecs::LoadFormats()
413{
414 const NDLL::CLibrary &lib = Libs.Back().Lib;
415
416 Func_GetHandlerProperty getProp = NULL;
417 Func_GetHandlerProperty2 getProp2;
418 MY_GET_FUNC (getProp2, Func_GetHandlerProperty2, lib.GetProc("GetHandlerProperty2"));
419 Func_GetIsArc getIsArc;
420 MY_GET_FUNC (getIsArc, Func_GetIsArc, lib.GetProc("GetIsArc"));
421
422 UInt32 numFormats = 1;
423
424 if (getProp2)
425 {
426 Func_GetNumberOfFormats getNumberOfFormats;
427 MY_GET_FUNC (getNumberOfFormats, Func_GetNumberOfFormats, lib.GetProc("GetNumberOfFormats"));
428 if (getNumberOfFormats)
429 {
430 RINOK(getNumberOfFormats(&numFormats));
431 }
432 }
433 else
434 {
435 MY_GET_FUNC (getProp, Func_GetHandlerProperty, lib.GetProc("GetHandlerProperty"));
436 if (!getProp)
437 return S_OK;
438 }
439
440 for (UInt32 i = 0; i < numFormats; i++)
441 {
442 CArcInfoEx item;
443 item.LibIndex = (int)(Libs.Size() - 1);
444 item.FormatIndex = i;
445
446 RINOK(GetProp_String(getProp, getProp2, i, NArchive::NHandlerPropID::kName, item.Name));
447
448 {
449 NCOM::CPropVariant prop;
450 if (GetProp(getProp, getProp2, i, NArchive::NHandlerPropID::kClassID, prop) != S_OK)
451 continue;
452 if (prop.vt != VT_BSTR)
453 continue;
454 if (::SysStringByteLen(prop.bstrVal) != sizeof(GUID))
455 return E_FAIL;
456 item.ClassID = *(const GUID *)(const void *)prop.bstrVal;
457 prop.Clear();
458 }
459
460 UString ext, addExt;
461 RINOK(GetProp_String(getProp, getProp2, i, NArchive::NHandlerPropID::kExtension, ext));
462 RINOK(GetProp_String(getProp, getProp2, i, NArchive::NHandlerPropID::kAddExtension, addExt));
463 item.AddExts(ext, addExt);
464
465 GetProp_Bool(getProp, getProp2, i, NArchive::NHandlerPropID::kUpdate, item.UpdateEnabled);
466 bool flags_Defined = false;
467 RINOK(GetProp_UInt32(getProp, getProp2, i, NArchive::NHandlerPropID::kFlags, item.Flags, flags_Defined));
468 item.NewInterface = flags_Defined;
469 if (!flags_Defined) // && item.UpdateEnabled
470 {
471 // support for DLL version before 9.31:
472 for (unsigned j = 0; j < ARRAY_SIZE(kArcFlagsPars); j += 2)
473 {
474 bool val = false;
475 GetProp_Bool(getProp, getProp2, i, kArcFlagsPars[j], val);
476 if (val)
477 item.Flags |= kArcFlagsPars[j + 1];
478 }
479 }
480
481 CByteBuffer sig;
482 RINOK(GetProp_RawData(getProp, getProp2, i, NArchive::NHandlerPropID::kSignature, sig));
483 if (sig.Size() != 0)
484 item.Signatures.Add(sig);
485 else
486 {
487 RINOK(GetProp_RawData(getProp, getProp2, i, NArchive::NHandlerPropID::kMultiSignature, sig));
488 ParseSignatures(sig, (unsigned)sig.Size(), item.Signatures);
489 }
490
491 bool signatureOffset_Defined;
492 RINOK(GetProp_UInt32(getProp, getProp2, i, NArchive::NHandlerPropID::kSignatureOffset, item.SignatureOffset, signatureOffset_Defined));
493
494 // bool version_Defined;
495 // RINOK(GetProp_UInt32(getProp, getProp2, i, NArchive::NHandlerPropID::kVersion, item.Version, version_Defined));
496
497 if (getIsArc)
498 getIsArc(i, &item.IsArcFunc);
499
500 Formats.Add(item);
501 }
502 return S_OK;
503}
504
505#ifdef _7ZIP_LARGE_PAGES
506extern "C"
507{
508 extern SIZE_T g_LargePageSize;
509}
510#endif
511
512
513void CCodecs::AddLastError(const FString &path)
514{
515 HRESULT res = GetLastError_noZero_HRESULT();
516 CCodecError &error = Errors.AddNew();
517 error.Path = path;
518 error.ErrorCode = res;
519}
520
521HRESULT CCodecs::LoadDll(const FString &dllPath, bool needCheckDll, bool *loadedOK)
522{
523 if (loadedOK)
524 *loadedOK = false;
525
526 // needCheckDll = 1;
527
528 #ifdef _WIN32
529 if (needCheckDll)
530 {
531 NDLL::CLibrary lib;
532 if (!lib.LoadEx(dllPath, LOAD_LIBRARY_AS_DATAFILE))
533 {
534 /* if is not win32
535 // %1 is not a valid Win32 application.
536 // #define ERROR_BAD_EXE_FORMAT 193L
537 */
538 // return GetLastError_noZero_HRESULT();
539 DWORD lastError = GetLastError();
540 if (lastError != ERROR_BAD_EXE_FORMAT)
541 {
542 CCodecError &error = Errors.AddNew();
543 error.Path = dllPath;
544 error.Message = "cannot load file as datafile library";
545 error.ErrorCode = HRESULT_FROM_WIN32(lastError);
546 }
547 return S_OK;
548 }
549 }
550 #else
551 UNUSED_VAR(needCheckDll)
552 #endif
553
554 Libs.AddNew();
555 CCodecLib &lib = Libs.Back();
556 lib.Path = dllPath;
557 bool used = false;
558 // HRESULT res = S_OK;
559
560 if (lib.Lib.Load(dllPath))
561 {
562 if (loadedOK)
563 *loadedOK = true;
564 #ifdef NEW_FOLDER_INTERFACE
565 lib.LoadIcons();
566 #endif
567
568 /*
569 {
570 Func_LibStartup _LibStartup;
571 MY_GET_FUNC (_LibStartup, Func_LibStartup, lib.Lib.GetProc("LibStartup"));
572 if (_LibStartup)
573 {
574 HRESULT res = _LibStartup();
575 if (res != 0)
576 {
577 CCodecError &error = Errors.AddNew();
578 error.Path = dllPath;
579 error.ErrorCode = res;
580 }
581 }
582 }
583 */
584
585 #ifdef _7ZIP_LARGE_PAGES
586 if (g_LargePageSize != 0)
587 {
588 Func_SetLargePageMode setLargePageMode;
589 MY_GET_FUNC (setLargePageMode, Func_SetLargePageMode, lib.Lib.GetProc("SetLargePageMode"));
590 if (setLargePageMode)
591 setLargePageMode();
592 }
593 #endif
594
595 if (CaseSensitiveChange)
596 {
597 Func_SetCaseSensitive setCaseSensitive;
598 MY_GET_FUNC (setCaseSensitive, Func_SetCaseSensitive, lib.Lib.GetProc("SetCaseSensitive"));
599 if (setCaseSensitive)
600 setCaseSensitive(CaseSensitive ? 1 : 0);
601 }
602
603 MY_GET_FUNC (lib.CreateObject, Func_CreateObject, lib.Lib.GetProc("CreateObject"));
604 {
605 unsigned startSize = Codecs.Size() + Hashers.Size();
606 HRESULT res = LoadCodecs();
607 if (startSize != Codecs.Size() + Hashers.Size())
608 used = true;
609 if (res == S_OK && lib.CreateObject)
610 {
611 startSize = Formats.Size();
612 res = LoadFormats();
613 if (startSize != Formats.Size())
614 used = true;
615 }
616 if (res != S_OK)
617 {
618 CCodecError &error = Errors.AddNew();
619 error.Path = dllPath;
620 error.ErrorCode = res;
621 }
622 }
623 // plugins can use non-7-zip dlls, so we silently ignore non7zip DLLs
624 /*
625 if (!used)
626 {
627 CCodecError &error = Errors.AddNew();
628 error.Path = dllPath;
629 error.Message = "no 7-Zip code";
630 }
631 */
632 }
633 else
634 {
635 AddLastError(dllPath);
636 }
637
638 if (!used)
639 Libs.DeleteBack();
640
641 return S_OK;
642}
643
644HRESULT CCodecs::LoadDllsFromFolder(const FString &folderPath)
645{
646 if (!NFile::NFind::DoesDirExist_FollowLink(folderPath))
647 // if (!NFile::NFind::DoesDirExist(folderPath))
648 {
649 // AddLastError(folderPath);
650 return S_OK;
651 }
652
653 FString folderPrefix = folderPath;
654 folderPrefix.Add_PathSepar();
655
656 NFile::NFind::CEnumerator enumerator;
657 enumerator.SetDirPrefix(folderPrefix);
658 NFile::NFind::CDirEntry fi;
659 for (;;)
660 {
661 bool found;
662 if (!enumerator.Next(fi, found))
663 {
664 // it can be wrong Symbolic link to folder here
665 AddLastError(folderPath);
666 break;
667 // return GetLastError_noZero_HRESULT();
668 }
669 if (!found)
670 break;
671 #ifdef _WIN32
672 if (fi.IsDir())
673 continue;
674 #else
675 if (enumerator.DirEntry_IsDir(fi, true)) // followLink
676 continue;
677 #endif
678
679 RINOK(LoadDll(folderPrefix + fi.Name, true));
680 }
681 return S_OK;
682}
683
684void CCodecs::CloseLibs()
685{
686 // OutputDebugStringA("~CloseLibs start");
687 /*
688 WIN32: FreeLibrary() (CLibrary::Free()) function doesn't work as expected,
689 if it's called from another FreeLibrary() call.
690 So we need to call FreeLibrary() before global destructors.
691
692 Also we free global links from DLLs to object of this module before CLibrary::Free() call.
693 */
694
695 FOR_VECTOR(i, Libs)
696 {
697 const CCodecLib &lib = Libs[i];
698 if (lib.SetCodecs)
699 lib.SetCodecs(NULL);
700 }
701
702 // OutputDebugStringA("~CloseLibs after SetCodecs");
703 Libs.Clear();
704 // OutputDebugStringA("~CloseLibs end");
705}
706
707#endif // EXTERNAL_CODECS
708
709
710HRESULT CCodecs::Load()
711{
712 #ifdef NEW_FOLDER_INTERFACE
713 InternalIcons.LoadIcons(g_hInstance);
714 #endif
715
716 Formats.Clear();
717
718 #ifdef EXTERNAL_CODECS
719 Errors.Clear();
720 MainDll_ErrorPath.Empty();
721 Codecs.Clear();
722 Hashers.Clear();
723 #endif
724
725 for (UInt32 i = 0; i < g_NumArcs; i++)
726 {
727 const CArcInfo &arc = *g_Arcs[i];
728 CArcInfoEx item;
729
730 item.Name = arc.Name;
731 item.CreateInArchive = arc.CreateInArchive;
732 item.IsArcFunc = arc.IsArc;
733 item.Flags = arc.Flags;
734
735 {
736 UString e, ae;
737 if (arc.Ext)
738 e = arc.Ext;
739 if (arc.AddExt)
740 ae = arc.AddExt;
741 item.AddExts(e, ae);
742 }
743
744 #ifndef _SFX
745
746 item.CreateOutArchive = arc.CreateOutArchive;
747 item.UpdateEnabled = (arc.CreateOutArchive != NULL);
748 item.SignatureOffset = arc.SignatureOffset;
749 // item.Version = MY_VER_MIX;
750 item.NewInterface = true;
751
752 if (arc.IsMultiSignature())
753 ParseSignatures(arc.Signature, arc.SignatureSize, item.Signatures);
754 else
755 {
756 if (arc.SignatureSize != 0) // 21.04
757 item.Signatures.AddNew().CopyFrom(arc.Signature, arc.SignatureSize);
758 }
759
760 #endif
761
762 Formats.Add(item);
763 }
764
765 // printf("\nLoad codecs \n");
766
767 #ifdef EXTERNAL_CODECS
768 const FString baseFolder = GetBaseFolderPrefixFromRegistry();
769 {
770 bool loadedOK;
771 RINOK(LoadDll(baseFolder + kMainDll, false, &loadedOK));
772 if (!loadedOK)
773 MainDll_ErrorPath = kMainDll;
774 }
775 RINOK(LoadDllsFromFolder(baseFolder + kCodecsFolderName));
776 RINOK(LoadDllsFromFolder(baseFolder + kFormatsFolderName));
777
778 NeedSetLibCodecs = true;
779
780 if (Libs.Size() == 0)
781 NeedSetLibCodecs = false;
782 else if (Libs.Size() == 1)
783 {
784 // we don't need to set ISetCompressCodecsInfo, if all arcs and codecs are in one external module.
785 #ifndef EXPORT_CODECS
786 if (g_NumArcs == 0)
787 NeedSetLibCodecs = false;
788 #endif
789 }
790
791 if (NeedSetLibCodecs)
792 {
793 /* 15.00: now we call global function in DLL: SetCompressCodecsInfo(c)
794 old versions called only ISetCompressCodecsInfo::SetCompressCodecsInfo(c) for each archive handler */
795
796 FOR_VECTOR(i, Libs)
797 {
798 CCodecLib &lib = Libs[i];
799 MY_GET_FUNC (lib.SetCodecs, Func_SetCodecs, lib.Lib.GetProc("SetCodecs"));
800 if (lib.SetCodecs)
801 {
802 RINOK(lib.SetCodecs(this));
803 }
804 }
805 }
806
807 #endif
808
809 // we sort Formats to get fixed order of Formats after compilation.
810 Formats.Sort();
811 return S_OK;
812}
813
814#ifndef _SFX
815
816int CCodecs::FindFormatForArchiveName(const UString &arcPath) const
817{
818 int dotPos = arcPath.ReverseFind_Dot();
819 if (dotPos <= arcPath.ReverseFind_PathSepar())
820 return -1;
821 const UString ext = arcPath.Ptr((unsigned)(dotPos + 1));
822 if (ext.IsEmpty())
823 return -1;
824 if (ext.IsEqualTo_Ascii_NoCase("exe"))
825 return -1;
826 FOR_VECTOR (i, Formats)
827 {
828 const CArcInfoEx &arc = Formats[i];
829 /*
830 if (!arc.UpdateEnabled)
831 continue;
832 */
833 if (arc.FindExtension(ext) >= 0)
834 return (int)i;
835 }
836 return -1;
837}
838
839int CCodecs::FindFormatForExtension(const UString &ext) const
840{
841 if (ext.IsEmpty())
842 return -1;
843 FOR_VECTOR (i, Formats)
844 if (Formats[i].FindExtension(ext) >= 0)
845 return (int)i;
846 return -1;
847}
848
849int CCodecs::FindFormatForArchiveType(const UString &arcType) const
850{
851 FOR_VECTOR (i, Formats)
852 if (Formats[i].Name.IsEqualTo_NoCase(arcType))
853 return (int)i;
854 return -1;
855}
856
857bool CCodecs::FindFormatForArchiveType(const UString &arcType, CIntVector &formatIndices) const
858{
859 formatIndices.Clear();
860 for (unsigned pos = 0; pos < arcType.Len();)
861 {
862 int pos2 = arcType.Find(L'.', pos);
863 if (pos2 < 0)
864 pos2 = (int)arcType.Len();
865 const UString name = arcType.Mid(pos, (unsigned)pos2 - pos);
866 if (name.IsEmpty())
867 return false;
868 int index = FindFormatForArchiveType(name);
869 if (index < 0 && name != L"*")
870 {
871 formatIndices.Clear();
872 return false;
873 }
874 formatIndices.Add(index);
875 pos = (unsigned)pos2 + 1;
876 }
877 return true;
878}
879
880#endif // _SFX
881
882
883#ifdef NEW_FOLDER_INTERFACE
884
885void CCodecIcons::LoadIcons(HMODULE m)
886{
887 UString iconTypes;
888 MyLoadString(m, kIconTypesResId, iconTypes);
889 UStringVector pairs;
890 SplitString(iconTypes, pairs);
891 FOR_VECTOR (i, pairs)
892 {
893 const UString &s = pairs[i];
894 int pos = s.Find(L':');
895 CIconPair iconPair;
896 iconPair.IconIndex = -1;
897 if (pos < 0)
898 pos = (int)s.Len();
899 else
900 {
901 const UString num = s.Ptr((unsigned)pos + 1);
902 if (!num.IsEmpty())
903 {
904 const wchar_t *end;
905 iconPair.IconIndex = (int)ConvertStringToUInt32(num, &end);
906 if (*end != 0)
907 continue;
908 }
909 }
910 iconPair.Ext = s.Left((unsigned)pos);
911 IconPairs.Add(iconPair);
912 }
913}
914
915bool CCodecIcons::FindIconIndex(const UString &ext, int &iconIndex) const
916{
917 iconIndex = -1;
918 FOR_VECTOR (i, IconPairs)
919 {
920 const CIconPair &pair = IconPairs[i];
921 if (ext.IsEqualTo_NoCase(pair.Ext))
922 {
923 iconIndex = pair.IconIndex;
924 return true;
925 }
926 }
927 return false;
928}
929
930#endif // NEW_FOLDER_INTERFACE
931
932
933#ifdef EXTERNAL_CODECS
934
935// #define EXPORT_CODECS
936
937#ifdef EXPORT_CODECS
938
939extern unsigned g_NumCodecs;
940STDAPI CreateDecoder(UInt32 index, const GUID *iid, void **outObject);
941STDAPI CreateEncoder(UInt32 index, const GUID *iid, void **outObject);
942STDAPI GetMethodProperty(UInt32 codecIndex, PROPID propID, PROPVARIANT *value);
943#define NUM_EXPORT_CODECS g_NumCodecs
944
945extern unsigned g_NumHashers;
946STDAPI CreateHasher(UInt32 index, IHasher **hasher);
947STDAPI GetHasherProp(UInt32 codecIndex, PROPID propID, PROPVARIANT *value);
948#define NUM_EXPORT_HASHERS g_NumHashers
949
950#else // EXPORT_CODECS
951
952#define NUM_EXPORT_CODECS 0
953#define NUM_EXPORT_HASHERS 0
954
955#endif // EXPORT_CODECS
956
957STDMETHODIMP CCodecs::GetNumMethods(UInt32 *numMethods)
958{
959 *numMethods = NUM_EXPORT_CODECS
960 #ifdef EXTERNAL_CODECS
961 + Codecs.Size()
962 #endif
963 ;
964 return S_OK;
965}
966
967STDMETHODIMP CCodecs::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
968{
969 #ifdef EXPORT_CODECS
970 if (index < g_NumCodecs)
971 return GetMethodProperty(index, propID, value);
972 #endif
973
974 #ifdef EXTERNAL_CODECS
975 const CDllCodecInfo &ci = Codecs[index - NUM_EXPORT_CODECS];
976
977 if (propID == NMethodPropID::kDecoderIsAssigned ||
978 propID == NMethodPropID::kEncoderIsAssigned)
979 {
980 NCOM::CPropVariant prop;
981 prop = (bool)((propID == NMethodPropID::kDecoderIsAssigned) ?
982 ci.DecoderIsAssigned :
983 ci.EncoderIsAssigned);
984 prop.Detach(value);
985 return S_OK;
986 }
987
988 if (propID == NMethodPropID::kIsFilter && ci.IsFilter_Assigned)
989 {
990 NCOM::CPropVariant prop;
991 prop = (bool)ci.IsFilter;
992 prop.Detach(value);
993 return S_OK;
994 }
995
996 const CCodecLib &lib = Libs[ci.LibIndex];
997 return lib.GetMethodProperty(ci.CodecIndex, propID, value);
998 #else
999 return E_FAIL;
1000 #endif
1001}
1002
1003STDMETHODIMP CCodecs::CreateDecoder(UInt32 index, const GUID *iid, void **coder)
1004{
1005 #ifdef EXPORT_CODECS
1006 if (index < g_NumCodecs)
1007 return CreateDecoder(index, iid, coder);
1008 #endif
1009
1010 #ifdef EXTERNAL_CODECS
1011 const CDllCodecInfo &ci = Codecs[index - NUM_EXPORT_CODECS];
1012 if (ci.DecoderIsAssigned)
1013 {
1014 const CCodecLib &lib = Libs[ci.LibIndex];
1015 if (lib.CreateDecoder)
1016 return lib.CreateDecoder(ci.CodecIndex, iid, (void **)coder);
1017 if (lib.CreateObject)
1018 return lib.CreateObject(&ci.Decoder, iid, (void **)coder);
1019 }
1020 return S_OK;
1021 #else
1022 return E_FAIL;
1023 #endif
1024}
1025
1026STDMETHODIMP CCodecs::CreateEncoder(UInt32 index, const GUID *iid, void **coder)
1027{
1028 #ifdef EXPORT_CODECS
1029 if (index < g_NumCodecs)
1030 return CreateEncoder(index, iid, coder);
1031 #endif
1032
1033 #ifdef EXTERNAL_CODECS
1034 const CDllCodecInfo &ci = Codecs[index - NUM_EXPORT_CODECS];
1035 if (ci.EncoderIsAssigned)
1036 {
1037 const CCodecLib &lib = Libs[ci.LibIndex];
1038 if (lib.CreateEncoder)
1039 return lib.CreateEncoder(ci.CodecIndex, iid, (void **)coder);
1040 if (lib.CreateObject)
1041 return lib.CreateObject(&ci.Encoder, iid, (void **)coder);
1042 }
1043 return S_OK;
1044 #else
1045 return E_FAIL;
1046 #endif
1047}
1048
1049
1050STDMETHODIMP_(UInt32) CCodecs::GetNumHashers()
1051{
1052 return NUM_EXPORT_HASHERS
1053 #ifdef EXTERNAL_CODECS
1054 + Hashers.Size()
1055 #endif
1056 ;
1057}
1058
1059STDMETHODIMP CCodecs::GetHasherProp(UInt32 index, PROPID propID, PROPVARIANT *value)
1060{
1061 #ifdef EXPORT_CODECS
1062 if (index < g_NumHashers)
1063 return ::GetHasherProp(index, propID, value);
1064 #endif
1065
1066 #ifdef EXTERNAL_CODECS
1067 const CDllHasherInfo &ci = Hashers[index - NUM_EXPORT_HASHERS];
1068 return Libs[ci.LibIndex].ComHashers->GetHasherProp(ci.HasherIndex, propID, value);
1069 #else
1070 return E_FAIL;
1071 #endif
1072}
1073
1074STDMETHODIMP CCodecs::CreateHasher(UInt32 index, IHasher **hasher)
1075{
1076 #ifdef EXPORT_CODECS
1077 if (index < g_NumHashers)
1078 return CreateHasher(index, hasher);
1079 #endif
1080 #ifdef EXTERNAL_CODECS
1081 const CDllHasherInfo &ci = Hashers[index - NUM_EXPORT_HASHERS];
1082 return Libs[ci.LibIndex].ComHashers->CreateHasher(ci.HasherIndex, hasher);
1083 #else
1084 return E_FAIL;
1085 #endif
1086}
1087
1088int CCodecs::GetCodec_LibIndex(UInt32 index) const
1089{
1090 #ifdef EXPORT_CODECS
1091 if (index < g_NumCodecs)
1092 return -1;
1093 #endif
1094
1095 #ifdef EXTERNAL_CODECS
1096 const CDllCodecInfo &ci = Codecs[index - NUM_EXPORT_CODECS];
1097 return (int)ci.LibIndex;
1098 #else
1099 return -1;
1100 #endif
1101}
1102
1103int CCodecs::GetHasherLibIndex(UInt32 index)
1104{
1105 #ifdef EXPORT_CODECS
1106 if (index < g_NumHashers)
1107 return -1;
1108 #endif
1109
1110 #ifdef EXTERNAL_CODECS
1111 const CDllHasherInfo &ci = Hashers[index - NUM_EXPORT_HASHERS];
1112 return (int)ci.LibIndex;
1113 #else
1114 return -1;
1115 #endif
1116}
1117
1118bool CCodecs::GetCodec_DecoderIsAssigned(UInt32 index) const
1119{
1120 #ifdef EXPORT_CODECS
1121 if (index < g_NumCodecs)
1122 {
1123 NCOM::CPropVariant prop;
1124 if (GetProperty(index, NMethodPropID::kDecoderIsAssigned, &prop) == S_OK)
1125 {
1126 if (prop.vt == VT_BOOL)
1127 return VARIANT_BOOLToBool(prop.boolVal);
1128 }
1129 return false;
1130 }
1131 #endif
1132
1133 #ifdef EXTERNAL_CODECS
1134 return Codecs[index - NUM_EXPORT_CODECS].DecoderIsAssigned;
1135 #else
1136 return false;
1137 #endif
1138}
1139
1140
1141bool CCodecs::GetCodec_EncoderIsAssigned(UInt32 index) const
1142{
1143 #ifdef EXPORT_CODECS
1144 if (index < g_NumCodecs)
1145 {
1146 NCOM::CPropVariant prop;
1147 if (GetProperty(index, NMethodPropID::kEncoderIsAssigned, &prop) == S_OK)
1148 {
1149 if (prop.vt == VT_BOOL)
1150 return VARIANT_BOOLToBool(prop.boolVal);
1151 }
1152 return false;
1153 }
1154 #endif
1155
1156 #ifdef EXTERNAL_CODECS
1157 return Codecs[index - NUM_EXPORT_CODECS].EncoderIsAssigned;
1158 #else
1159 return false;
1160 #endif
1161}
1162
1163
1164bool CCodecs::GetCodec_IsFilter(UInt32 index, bool &isAssigned) const
1165{
1166 isAssigned = false;
1167 #ifdef EXPORT_CODECS
1168 if (index < g_NumCodecs)
1169 {
1170 NCOM::CPropVariant prop;
1171 if (GetProperty(index, NMethodPropID::kIsFilter, &prop) == S_OK)
1172 {
1173 if (prop.vt == VT_BOOL)
1174 {
1175 isAssigned = true;
1176 return VARIANT_BOOLToBool(prop.boolVal);
1177 }
1178 }
1179 return false;
1180 }
1181 #endif
1182
1183 #ifdef EXTERNAL_CODECS
1184 {
1185 const CDllCodecInfo &c = Codecs[index - NUM_EXPORT_CODECS];
1186 isAssigned = c.IsFilter_Assigned;
1187 return c.IsFilter;
1188 }
1189 #else
1190 return false;
1191 #endif
1192}
1193
1194
1195UInt32 CCodecs::GetCodec_NumStreams(UInt32 index)
1196{
1197 NCOM::CPropVariant prop;
1198 if (GetProperty(index, NMethodPropID::kPackStreams, &prop) != S_OK)
1199 return 0;
1200 if (prop.vt == VT_UI4)
1201 return (UInt32)prop.ulVal;
1202 if (prop.vt == VT_EMPTY)
1203 return 1;
1204 return 0;
1205}
1206
1207HRESULT CCodecs::GetCodec_Id(UInt32 index, UInt64 &id)
1208{
1209 NCOM::CPropVariant prop;
1210 RINOK(GetProperty(index, NMethodPropID::kID, &prop));
1211 if (prop.vt != VT_UI8)
1212 return E_INVALIDARG;
1213 id = prop.uhVal.QuadPart;
1214 return S_OK;
1215}
1216
1217AString CCodecs::GetCodec_Name(UInt32 index)
1218{
1219 AString s;
1220 NCOM::CPropVariant prop;
1221 if (GetProperty(index, NMethodPropID::kName, &prop) == S_OK)
1222 if (prop.vt == VT_BSTR)
1223 s.SetFromWStr_if_Ascii(prop.bstrVal);
1224 return s;
1225}
1226
1227UInt64 CCodecs::GetHasherId(UInt32 index)
1228{
1229 NCOM::CPropVariant prop;
1230 if (GetHasherProp(index, NMethodPropID::kID, &prop) != S_OK)
1231 return 0;
1232 if (prop.vt != VT_UI8)
1233 return 0;
1234 return prop.uhVal.QuadPart;
1235}
1236
1237AString CCodecs::GetHasherName(UInt32 index)
1238{
1239 AString s;
1240 NCOM::CPropVariant prop;
1241 if (GetHasherProp(index, NMethodPropID::kName, &prop) == S_OK)
1242 if (prop.vt == VT_BSTR)
1243 s.SetFromWStr_if_Ascii(prop.bstrVal);
1244 return s;
1245}
1246
1247UInt32 CCodecs::GetHasherDigestSize(UInt32 index)
1248{
1249 NCOM::CPropVariant prop;
1250 if (GetHasherProp(index, NMethodPropID::kDigestSize, &prop) != S_OK)
1251 return 0;
1252 if (prop.vt != VT_UI4)
1253 return 0;
1254 return prop.ulVal;
1255}
1256
1257void CCodecs::GetCodecsErrorMessage(UString &s)
1258{
1259 s.Empty();
1260 FOR_VECTOR (i, Errors)
1261 {
1262 const CCodecError &ce = Errors[i];
1263 s += "Codec Load Error: ";
1264 s += fs2us(ce.Path);
1265 if (ce.ErrorCode != 0)
1266 {
1267 s += " : ";
1268 s += NWindows::NError::MyFormatMessage(ce.ErrorCode);
1269 }
1270 if (!ce.Message.IsEmpty())
1271 {
1272 s += " : ";
1273 s += ce.Message;
1274 }
1275 s.Add_LF();
1276 }
1277}
1278
1279#endif // EXTERNAL_CODECS
1280
1281#ifndef _SFX
1282
1283extern unsigned g_NumCodecs;
1284extern const CCodecInfo *g_Codecs[];
1285
1286void CCodecs::Get_CodecsInfoUser_Vector(CObjectVector<CCodecInfoUser> &v)
1287{
1288 v.Clear();
1289 {
1290 for (unsigned i = 0; i < g_NumCodecs; i++)
1291 {
1292 const CCodecInfo &cod = *g_Codecs[i];
1293 CCodecInfoUser &u = v.AddNew();
1294 u.EncoderIsAssigned = (cod.CreateEncoder != NULL);
1295 u.DecoderIsAssigned = (cod.CreateDecoder != NULL);
1296 u.IsFilter_Assigned = true;
1297 u.IsFilter = cod.IsFilter;
1298 u.NumStreams = cod.NumStreams;
1299 u.Name = cod.Name;
1300 }
1301 }
1302
1303
1304 #ifdef EXTERNAL_CODECS
1305 {
1306 UInt32 numMethods;
1307 if (GetNumMethods(&numMethods) == S_OK)
1308 for (UInt32 j = 0; j < numMethods; j++)
1309 {
1310 CCodecInfoUser &u = v.AddNew();
1311 u.EncoderIsAssigned = GetCodec_EncoderIsAssigned(j);
1312 u.DecoderIsAssigned = GetCodec_DecoderIsAssigned(j);
1313 u.IsFilter = GetCodec_IsFilter(j, u.IsFilter_Assigned);
1314 u.NumStreams = GetCodec_NumStreams(j);
1315 u.Name = GetCodec_Name(j);
1316 }
1317 }
1318 #endif
1319}
1320
1321#endif
diff --git a/CPP/7zip/UI/Common/LoadCodecs.h b/CPP/7zip/UI/Common/LoadCodecs.h
new file mode 100644
index 0000000..829472d
--- /dev/null
+++ b/CPP/7zip/UI/Common/LoadCodecs.h
@@ -0,0 +1,474 @@
1// LoadCodecs.h
2
3#ifndef __LOAD_CODECS_H
4#define __LOAD_CODECS_H
5
6/*
7Client application uses LoadCodecs.* to load plugins to
8CCodecs object, that contains 3 lists of plugins:
9 1) Formats - internal and external archive handlers
10 2) Codecs - external codecs
11 3) Hashers - external hashers
12
13EXTERNAL_CODECS
14---------------
15
16 if EXTERNAL_CODECS is defined, then the code tries to load external
17 plugins from DLL files (shared libraries).
18
19 There are two types of executables in 7-Zip:
20
21 1) Executable that uses external plugins must be compiled
22 with EXTERNAL_CODECS defined:
23 - 7z.exe, 7zG.exe, 7zFM.exe
24
25 Note: EXTERNAL_CODECS is used also in CPP/7zip/Common/CreateCoder.h
26 that code is used in plugin module (7z.dll).
27
28 2) Standalone modules are compiled without EXTERNAL_CODECS:
29 - SFX modules: 7z.sfx, 7zCon.sfx
30 - standalone versions of console 7-Zip: 7za.exe, 7zr.exe
31
32 if EXTERNAL_CODECS is defined, CCodecs class implements interfaces:
33 - ICompressCodecsInfo : for Codecs
34 - IHashers : for Hashers
35
36 The client application can send CCodecs object to each plugin module.
37 And plugin module can use ICompressCodecsInfo or IHashers interface to access
38 another plugins.
39
40 There are 2 ways to send (ICompressCodecsInfo * compressCodecsInfo) to plugin
41 1) for old versions:
42 a) request ISetCompressCodecsInfo from created archive handler.
43 b) call ISetCompressCodecsInfo::SetCompressCodecsInfo(compressCodecsInfo)
44 2) for new versions:
45 a) request "SetCodecs" function from DLL file
46 b) call SetCodecs(compressCodecsInfo) function from DLL file
47*/
48
49#include "../../../Common/MyBuffer.h"
50#include "../../../Common/MyCom.h"
51#include "../../../Common/MyString.h"
52#include "../../../Common/ComTry.h"
53
54#ifdef EXTERNAL_CODECS
55#include "../../../Windows/DLL.h"
56#endif
57
58#include "../../ICoder.h"
59
60#include "../../Archive/IArchive.h"
61
62
63#ifdef EXTERNAL_CODECS
64
65struct CDllCodecInfo
66{
67 unsigned LibIndex;
68 UInt32 CodecIndex;
69 bool EncoderIsAssigned;
70 bool DecoderIsAssigned;
71 bool IsFilter;
72 bool IsFilter_Assigned;
73 CLSID Encoder;
74 CLSID Decoder;
75};
76
77struct CDllHasherInfo
78{
79 unsigned LibIndex;
80 UInt32 HasherIndex;
81};
82
83#endif
84
85struct CArcExtInfo
86{
87 UString Ext;
88 UString AddExt;
89
90 CArcExtInfo() {}
91 CArcExtInfo(const UString &ext): Ext(ext) {}
92 CArcExtInfo(const UString &ext, const UString &addExt): Ext(ext), AddExt(addExt) {}
93};
94
95
96struct CArcInfoEx
97{
98 UInt32 Flags;
99
100 Func_CreateInArchive CreateInArchive;
101 Func_IsArc IsArcFunc;
102
103 UString Name;
104 CObjectVector<CArcExtInfo> Exts;
105
106 #ifndef _SFX
107 Func_CreateOutArchive CreateOutArchive;
108 bool UpdateEnabled;
109 bool NewInterface;
110 // UInt32 Version;
111 UInt32 SignatureOffset;
112 CObjectVector<CByteBuffer> Signatures;
113 #ifdef NEW_FOLDER_INTERFACE
114 UStringVector AssociateExts;
115 #endif
116 #endif
117
118 #ifdef EXTERNAL_CODECS
119 int LibIndex;
120 UInt32 FormatIndex;
121 CLSID ClassID;
122 #endif
123
124 int Compare(const CArcInfoEx &a) const
125 {
126 int res = Name.Compare(a.Name);
127 if (res != 0)
128 return res;
129 #ifdef EXTERNAL_CODECS
130 return MyCompare(LibIndex, a.LibIndex);
131 #else
132 return 0;
133 #endif
134 /*
135 if (LibIndex < a.LibIndex) return -1;
136 if (LibIndex > a.LibIndex) return 1;
137 return 0;
138 */
139 }
140
141 bool Flags_KeepName() const { return (Flags & NArcInfoFlags::kKeepName) != 0; }
142 bool Flags_FindSignature() const { return (Flags & NArcInfoFlags::kFindSignature) != 0; }
143
144 bool Flags_AltStreams() const { return (Flags & NArcInfoFlags::kAltStreams) != 0; }
145 bool Flags_NtSecure() const { return (Flags & NArcInfoFlags::kNtSecure) != 0; }
146 bool Flags_SymLinks() const { return (Flags & NArcInfoFlags::kSymLinks) != 0; }
147 bool Flags_HardLinks() const { return (Flags & NArcInfoFlags::kHardLinks) != 0; }
148
149 bool Flags_UseGlobalOffset() const { return (Flags & NArcInfoFlags::kUseGlobalOffset) != 0; }
150 bool Flags_StartOpen() const { return (Flags & NArcInfoFlags::kStartOpen) != 0; }
151 bool Flags_BackwardOpen() const { return (Flags & NArcInfoFlags::kBackwardOpen) != 0; }
152 bool Flags_PreArc() const { return (Flags & NArcInfoFlags::kPreArc) != 0; }
153 bool Flags_PureStartOpen() const { return (Flags & NArcInfoFlags::kPureStartOpen) != 0; }
154 bool Flags_ByExtOnlyOpen() const { return (Flags & NArcInfoFlags::kByExtOnlyOpen) != 0; }
155 bool Flags_HashHandler() const { return (Flags & NArcInfoFlags::kHashHandler) != 0; }
156
157 UString GetMainExt() const
158 {
159 if (Exts.IsEmpty())
160 return UString();
161 return Exts[0].Ext;
162 }
163 int FindExtension(const UString &ext) const;
164
165 /*
166 UString GetAllExtensions() const
167 {
168 UString s;
169 for (int i = 0; i < Exts.Size(); i++)
170 {
171 if (i > 0)
172 s += ' ';
173 s += Exts[i].Ext;
174 }
175 return s;
176 }
177 */
178
179 void AddExts(const UString &ext, const UString &addExt);
180
181 bool IsSplit() const { return StringsAreEqualNoCase_Ascii(Name, "Split"); }
182 // bool IsRar() const { return StringsAreEqualNoCase_Ascii(Name, "Rar"); }
183
184 CArcInfoEx():
185 Flags(0),
186 CreateInArchive(NULL),
187 IsArcFunc(NULL)
188 #ifndef _SFX
189 , CreateOutArchive(NULL)
190 , UpdateEnabled(false)
191 , NewInterface(false)
192 // , Version(0)
193 , SignatureOffset(0)
194 #endif
195 #ifdef EXTERNAL_CODECS
196 , LibIndex(-1)
197 #endif
198 {}
199};
200
201#ifdef NEW_FOLDER_INTERFACE
202
203struct CCodecIcons
204{
205 struct CIconPair
206 {
207 UString Ext;
208 int IconIndex;
209 };
210 CObjectVector<CIconPair> IconPairs;
211
212 void LoadIcons(HMODULE m);
213 bool FindIconIndex(const UString &ext, int &iconIndex) const;
214};
215
216#endif
217
218#ifdef EXTERNAL_CODECS
219
220struct CCodecLib
221 #ifdef NEW_FOLDER_INTERFACE
222 : public CCodecIcons
223 #endif
224{
225 NWindows::NDLL::CLibrary Lib;
226 FString Path;
227
228 Func_CreateObject CreateObject;
229 Func_GetMethodProperty GetMethodProperty;
230 Func_CreateDecoder CreateDecoder;
231 Func_CreateEncoder CreateEncoder;
232 Func_SetCodecs SetCodecs;
233
234 CMyComPtr<IHashers> ComHashers;
235
236 #ifdef NEW_FOLDER_INTERFACE
237 void LoadIcons() { CCodecIcons::LoadIcons((HMODULE)Lib); }
238 #endif
239
240 CCodecLib():
241 CreateObject(NULL),
242 GetMethodProperty(NULL),
243 CreateDecoder(NULL),
244 CreateEncoder(NULL),
245 SetCodecs(NULL)
246 {}
247};
248
249#endif
250
251struct CCodecError
252{
253 FString Path;
254 HRESULT ErrorCode;
255 AString Message;
256 CCodecError(): ErrorCode(0) {}
257};
258
259
260struct CCodecInfoUser
261{
262 // unsigned LibIndex;
263 // UInt32 CodecIndex;
264 // UInt64 id;
265 bool EncoderIsAssigned;
266 bool DecoderIsAssigned;
267 bool IsFilter;
268 bool IsFilter_Assigned;
269 UInt32 NumStreams;
270 AString Name;
271};
272
273
274class CCodecs:
275 #ifdef EXTERNAL_CODECS
276 public ICompressCodecsInfo,
277 public IHashers,
278 #else
279 public IUnknown,
280 #endif
281 public CMyUnknownImp
282{
283 CLASS_NO_COPY(CCodecs);
284public:
285 #ifdef EXTERNAL_CODECS
286
287 CObjectVector<CCodecLib> Libs;
288 FString MainDll_ErrorPath;
289 CObjectVector<CCodecError> Errors;
290
291 void AddLastError(const FString &path);
292 void CloseLibs();
293
294 class CReleaser
295 {
296 CLASS_NO_COPY(CReleaser);
297
298 /* CCodecsReleaser object releases CCodecs links.
299 1) CCodecs is COM object that is deleted when all links to that object will be released/
300 2) CCodecs::Libs[i] can hold (ICompressCodecsInfo *) link to CCodecs object itself.
301 To break that reference loop, we must close all CCodecs::Libs in CCodecsReleaser desttructor. */
302
303 CCodecs *_codecs;
304
305 public:
306 CReleaser(): _codecs(NULL) {}
307 void Set(CCodecs *codecs) { _codecs = codecs; }
308 ~CReleaser() { if (_codecs) _codecs->CloseLibs(); }
309 };
310
311 bool NeedSetLibCodecs; // = false, if we don't need to set codecs for archive handler via ISetCompressCodecsInfo
312
313 HRESULT LoadCodecs();
314 HRESULT LoadFormats();
315 HRESULT LoadDll(const FString &path, bool needCheckDll, bool *loadedOK = NULL);
316 HRESULT LoadDllsFromFolder(const FString &folderPrefix);
317
318 HRESULT CreateArchiveHandler(const CArcInfoEx &ai, bool outHandler, void **archive) const
319 {
320 return Libs[(unsigned)ai.LibIndex].CreateObject(&ai.ClassID, outHandler ? &IID_IOutArchive : &IID_IInArchive, (void **)archive);
321 }
322
323 #endif
324
325 #ifdef NEW_FOLDER_INTERFACE
326 CCodecIcons InternalIcons;
327 #endif
328
329 CObjectVector<CArcInfoEx> Formats;
330
331 #ifdef EXTERNAL_CODECS
332 CRecordVector<CDllCodecInfo> Codecs;
333 CRecordVector<CDllHasherInfo> Hashers;
334 #endif
335
336 bool CaseSensitiveChange;
337 bool CaseSensitive;
338
339 CCodecs():
340 #ifdef EXTERNAL_CODECS
341 NeedSetLibCodecs(true),
342 #endif
343 CaseSensitiveChange(false),
344 CaseSensitive(false)
345 {}
346
347 ~CCodecs()
348 {
349 // OutputDebugStringA("~CCodecs");
350 }
351
352 const wchar_t *GetFormatNamePtr(int formatIndex) const
353 {
354 return formatIndex < 0 ? L"#" : (const wchar_t *)Formats[(unsigned)formatIndex].Name;
355 }
356
357 HRESULT Load();
358
359 #ifndef _SFX
360 int FindFormatForArchiveName(const UString &arcPath) const;
361 int FindFormatForExtension(const UString &ext) const;
362 int FindFormatForArchiveType(const UString &arcType) const;
363 bool FindFormatForArchiveType(const UString &arcType, CIntVector &formatIndices) const;
364 #endif
365
366 #ifdef EXTERNAL_CODECS
367
368 MY_UNKNOWN_IMP2(ICompressCodecsInfo, IHashers)
369
370 STDMETHOD(GetNumMethods)(UInt32 *numMethods);
371 STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value);
372 STDMETHOD(CreateDecoder)(UInt32 index, const GUID *iid, void **coder);
373 STDMETHOD(CreateEncoder)(UInt32 index, const GUID *iid, void **coder);
374
375 STDMETHOD_(UInt32, GetNumHashers)();
376 STDMETHOD(GetHasherProp)(UInt32 index, PROPID propID, PROPVARIANT *value);
377 STDMETHOD(CreateHasher)(UInt32 index, IHasher **hasher);
378
379 #else
380
381 MY_UNKNOWN_IMP
382
383 #endif // EXTERNAL_CODECS
384
385
386 #ifdef EXTERNAL_CODECS
387
388 int GetCodec_LibIndex(UInt32 index) const;
389 bool GetCodec_DecoderIsAssigned(UInt32 index) const;
390 bool GetCodec_EncoderIsAssigned(UInt32 index) const;
391 bool GetCodec_IsFilter(UInt32 index, bool &isAssigned) const;
392 UInt32 GetCodec_NumStreams(UInt32 index);
393 HRESULT GetCodec_Id(UInt32 index, UInt64 &id);
394 AString GetCodec_Name(UInt32 index);
395
396 int GetHasherLibIndex(UInt32 index);
397 UInt64 GetHasherId(UInt32 index);
398 AString GetHasherName(UInt32 index);
399 UInt32 GetHasherDigestSize(UInt32 index);
400
401 void GetCodecsErrorMessage(UString &s);
402
403 #endif
404
405 HRESULT CreateInArchive(unsigned formatIndex, CMyComPtr<IInArchive> &archive) const
406 {
407 const CArcInfoEx &ai = Formats[formatIndex];
408 #ifdef EXTERNAL_CODECS
409 if (ai.LibIndex < 0)
410 #endif
411 {
412 COM_TRY_BEGIN
413 archive = ai.CreateInArchive();
414 return S_OK;
415 COM_TRY_END
416 }
417 #ifdef EXTERNAL_CODECS
418 return CreateArchiveHandler(ai, false, (void **)&archive);
419 #endif
420 }
421
422 #ifndef _SFX
423
424 HRESULT CreateOutArchive(unsigned formatIndex, CMyComPtr<IOutArchive> &archive) const
425 {
426 const CArcInfoEx &ai = Formats[formatIndex];
427 #ifdef EXTERNAL_CODECS
428 if (ai.LibIndex < 0)
429 #endif
430 {
431 COM_TRY_BEGIN
432 archive = ai.CreateOutArchive();
433 return S_OK;
434 COM_TRY_END
435 }
436
437 #ifdef EXTERNAL_CODECS
438 return CreateArchiveHandler(ai, true, (void **)&archive);
439 #endif
440 }
441
442 int FindOutFormatFromName(const UString &name) const
443 {
444 FOR_VECTOR (i, Formats)
445 {
446 const CArcInfoEx &arc = Formats[i];
447 if (!arc.UpdateEnabled)
448 continue;
449 if (arc.Name.IsEqualTo_NoCase(name))
450 return (int)i;
451 }
452 return -1;
453 }
454
455 void Get_CodecsInfoUser_Vector(CObjectVector<CCodecInfoUser> &v);
456
457 #endif // _SFX
458};
459
460#ifdef EXTERNAL_CODECS
461 #define CREATE_CODECS_OBJECT \
462 CCodecs *codecs = new CCodecs; \
463 CExternalCodecs __externalCodecs; \
464 __externalCodecs.GetCodecs = codecs; \
465 __externalCodecs.GetHashers = codecs; \
466 CCodecs::CReleaser codecsReleaser; \
467 codecsReleaser.Set(codecs);
468#else
469 #define CREATE_CODECS_OBJECT \
470 CCodecs *codecs = new CCodecs; \
471 CMyComPtr<IUnknown> __codecsRef = codecs;
472#endif
473
474#endif
diff --git a/CPP/7zip/UI/Common/OpenArchive.cpp b/CPP/7zip/UI/Common/OpenArchive.cpp
new file mode 100644
index 0000000..331793f
--- /dev/null
+++ b/CPP/7zip/UI/Common/OpenArchive.cpp
@@ -0,0 +1,3652 @@
1// OpenArchive.cpp
2
3#include "StdAfx.h"
4
5// #define SHOW_DEBUG_INFO
6
7#ifdef SHOW_DEBUG_INFO
8#include <stdio.h>
9#endif
10
11#include "../../../../C/CpuArch.h"
12
13#include "../../../Common/ComTry.h"
14#include "../../../Common/IntToString.h"
15#include "../../../Common/StringConvert.h"
16#include "../../../Common/StringToInt.h"
17#include "../../../Common/UTFConvert.h"
18#include "../../../Common/Wildcard.h"
19
20#include "../../../Windows/FileDir.h"
21
22#include "../../Common/FileStreams.h"
23#include "../../Common/LimitedStreams.h"
24#include "../../Common/ProgressUtils.h"
25#include "../../Common/StreamUtils.h"
26
27#include "../../Compress/CopyCoder.h"
28
29#include "DefaultName.h"
30#include "OpenArchive.h"
31
32#ifndef _SFX
33#include "SetProperties.h"
34#endif
35
36#ifndef _SFX
37#ifdef SHOW_DEBUG_INFO
38#define PRF(x) x
39#else
40#define PRF(x)
41#endif
42#endif
43
44// increase it, if you need to support larger SFX stubs
45static const UInt64 kMaxCheckStartPosition = 1 << 23;
46
47/*
48Open:
49 - formatIndex >= 0 (exact Format)
50 1) Open with main type. Archive handler is allowed to use archive start finder.
51 Warning, if there is tail.
52
53 - formatIndex = -1 (Parser:0) (default)
54 - same as #1 but doesn't return Parser
55
56 - formatIndex = -2 (#1)
57 - file has supported extension (like a.7z)
58 Open with that main type (only starting from start of file).
59 - open OK:
60 - if there is no tail - return OK
61 - if there is tail:
62 - archive is not "Self Exe" - return OK with Warning, that there is tail
63 - archive is "Self Exe"
64 ignore "Self Exe" stub, and tries to open tail
65 - tail can be open as archive - shows that archive and stub size property.
66 - tail can't be open as archive - shows Parser ???
67 - open FAIL:
68 Try to open with all other types from offset 0 only.
69 If some open type is OK and physical archive size is uequal or larger
70 than file size, then return that archive with warning that cannot be open as [extension type].
71 If extension was EXE, it will try to open as unknown_extension case
72 - file has unknown extension (like a.hhh)
73 It tries to open via parser code.
74 - if there is full archive or tail archive and unknown block or "Self Exe"
75 at front, it shows tail archive and stub size property.
76 - in another cases, if there is some archive inside file, it returns parser/
77 - in another cases, it retuens S_FALSE
78
79
80 - formatIndex = -3 (#2)
81 - same as #1, but
82 - stub (EXE) + archive is open in Parser
83
84 - formatIndex = -4 (#3)
85 - returns only Parser. skip full file archive. And show other sub-archives
86
87 - formatIndex = -5 (#4)
88 - returns only Parser. skip full file archive. And show other sub-archives for each byte pos
89
90*/
91
92
93
94
95using namespace NWindows;
96
97/*
98#ifdef _SFX
99#define OPEN_PROPS_PARAM
100#else
101#define OPEN_PROPS_PARAM , props
102#endif
103*/
104
105/*
106CArc::~CArc()
107{
108 GetRawProps.Release();
109 Archive.Release();
110 printf("\nCArc::~CArc()\n");
111}
112*/
113
114#ifndef _SFX
115
116namespace NArchive {
117namespace NParser {
118
119struct CParseItem
120{
121 UInt64 Offset;
122 UInt64 Size;
123 // UInt64 OkSize;
124 UString Name;
125 UString Extension;
126 FILETIME FileTime;
127 UString Comment;
128 UString ArcType;
129
130 bool FileTime_Defined;
131 bool UnpackSize_Defined;
132 bool NumSubDirs_Defined;
133 bool NumSubFiles_Defined;
134
135 bool IsSelfExe;
136 bool IsNotArcType;
137
138 UInt64 UnpackSize;
139 UInt64 NumSubDirs;
140 UInt64 NumSubFiles;
141
142 int FormatIndex;
143
144 bool LenIsUnknown;
145
146 CParseItem():
147 // OkSize(0),
148 FileTime_Defined(false),
149 UnpackSize_Defined(false),
150 NumSubDirs_Defined(false),
151 NumSubFiles_Defined(false),
152 IsSelfExe(false),
153 IsNotArcType(false),
154 LenIsUnknown(false)
155 {}
156
157 /*
158 bool IsEqualTo(const CParseItem &item) const
159 {
160 return Offset == item.Offset && Size == item.Size;
161 }
162 */
163
164 void NormalizeOffset()
165 {
166 if ((Int64)Offset < 0)
167 {
168 Size += Offset;
169 // OkSize += Offset;
170 Offset = 0;
171 }
172 }
173};
174
175class CHandler:
176 public IInArchive,
177 public IInArchiveGetStream,
178 public CMyUnknownImp
179{
180public:
181 CObjectVector<CParseItem> _items;
182 UInt64 _maxEndOffset;
183 CMyComPtr<IInStream> _stream;
184
185 MY_UNKNOWN_IMP2(
186 IInArchive,
187 IInArchiveGetStream)
188
189 INTERFACE_IInArchive(;)
190 STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
191
192 UInt64 GetLastEnd() const
193 {
194 if (_items.IsEmpty())
195 return 0;
196 const CParseItem &back = _items.Back();
197 return back.Offset + back.Size;
198 }
199
200 void AddUnknownItem(UInt64 next);
201 int FindInsertPos(const CParseItem &item) const;
202 void AddItem(const CParseItem &item);
203
204 CHandler(): _maxEndOffset(0) {}
205};
206
207int CHandler::FindInsertPos(const CParseItem &item) const
208{
209 unsigned left = 0, right = _items.Size();
210 while (left != right)
211 {
212 unsigned mid = (left + right) / 2;
213 const CParseItem & midItem = _items[mid];
214 if (item.Offset < midItem.Offset)
215 right = mid;
216 else if (item.Offset > midItem.Offset)
217 left = mid + 1;
218 else if (item.Size < midItem.Size)
219 right = mid;
220 /*
221 else if (item.Size > midItem.Size)
222 left = mid + 1;
223 */
224 else
225 {
226 left = mid + 1;
227 // return -1;
228 }
229 }
230 return (int)left;
231}
232
233void CHandler::AddUnknownItem(UInt64 next)
234{
235 /*
236 UInt64 prevEnd = 0;
237 if (!_items.IsEmpty())
238 {
239 const CParseItem &back = _items.Back();
240 prevEnd = back.Offset + back.Size;
241 }
242 */
243 if (_maxEndOffset < next)
244 {
245 CParseItem item2;
246 item2.Offset = _maxEndOffset;
247 item2.Size = next - _maxEndOffset;
248 _maxEndOffset = next;
249 _items.Add(item2);
250 }
251 else if (_maxEndOffset > next && !_items.IsEmpty())
252 {
253 CParseItem &back = _items.Back();
254 if (back.LenIsUnknown)
255 {
256 back.Size = next - back.Offset;
257 _maxEndOffset = next;
258 }
259 }
260}
261
262void CHandler::AddItem(const CParseItem &item)
263{
264 AddUnknownItem(item.Offset);
265 int pos = FindInsertPos(item);
266 if (pos >= 0)
267 {
268 _items.Insert((unsigned)pos, item);
269 UInt64 next = item.Offset + item.Size;
270 if (_maxEndOffset < next)
271 _maxEndOffset = next;
272 }
273}
274
275/*
276static const CStatProp kProps[] =
277{
278 { NULL, kpidPath, VT_BSTR},
279 { NULL, kpidSize, VT_UI8},
280 { NULL, kpidMTime, VT_FILETIME},
281 { NULL, kpidType, VT_BSTR},
282 { NULL, kpidComment, VT_BSTR},
283 { NULL, kpidOffset, VT_UI8},
284 { NULL, kpidUnpackSize, VT_UI8},
285// { NULL, kpidNumSubDirs, VT_UI8},
286};
287*/
288
289static const Byte kProps[] =
290{
291 kpidPath,
292 kpidSize,
293 kpidMTime,
294 kpidType,
295 kpidComment,
296 kpidOffset,
297 kpidUnpackSize
298};
299
300IMP_IInArchive_Props
301IMP_IInArchive_ArcProps_NO
302
303STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback * /* openArchiveCallback */)
304{
305 COM_TRY_BEGIN
306 {
307 Close();
308 _stream = stream;
309 }
310 return S_OK;
311 COM_TRY_END
312}
313
314STDMETHODIMP CHandler::Close()
315{
316 _items.Clear();
317 _stream.Release();
318 return S_OK;
319}
320
321STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
322{
323 *numItems = _items.Size();
324 return S_OK;
325}
326
327STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
328{
329 COM_TRY_BEGIN
330 NCOM::CPropVariant prop;
331
332 const CParseItem &item = _items[index];
333
334 switch (propID)
335 {
336 case kpidPath:
337 {
338 char sz[32];
339 ConvertUInt32ToString(index + 1, sz);
340 UString s(sz);
341 if (!item.Name.IsEmpty())
342 {
343 s += '.';
344 s += item.Name;
345 }
346 if (!item.Extension.IsEmpty())
347 {
348 s += '.';
349 s += item.Extension;
350 }
351 prop = s; break;
352 }
353 case kpidSize:
354 case kpidPackSize: prop = item.Size; break;
355 case kpidOffset: prop = item.Offset; break;
356 case kpidUnpackSize: if (item.UnpackSize_Defined) prop = item.UnpackSize; break;
357 case kpidNumSubFiles: if (item.NumSubFiles_Defined) prop = item.NumSubFiles; break;
358 case kpidNumSubDirs: if (item.NumSubDirs_Defined) prop = item.NumSubDirs; break;
359 case kpidMTime: if (item.FileTime_Defined) prop = item.FileTime; break;
360 case kpidComment: if (!item.Comment.IsEmpty()) prop = item.Comment; break;
361 case kpidType: if (!item.ArcType.IsEmpty()) prop = item.ArcType; break;
362 }
363 prop.Detach(value);
364 return S_OK;
365 COM_TRY_END
366}
367
368HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems,
369 Int32 testMode, IArchiveExtractCallback *extractCallback)
370{
371 COM_TRY_BEGIN
372
373 bool allFilesMode = (numItems == (UInt32)(Int32)-1);
374 if (allFilesMode)
375 numItems = _items.Size();
376 if (_stream && numItems == 0)
377 return S_OK;
378 UInt64 totalSize = 0;
379 UInt32 i;
380 for (i = 0; i < numItems; i++)
381 totalSize += _items[allFilesMode ? i : indices[i]].Size;
382 extractCallback->SetTotal(totalSize);
383
384 totalSize = 0;
385
386 CLocalProgress *lps = new CLocalProgress;
387 CMyComPtr<ICompressProgressInfo> progress = lps;
388 lps->Init(extractCallback, false);
389
390 CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
391 CMyComPtr<ISequentialInStream> inStream(streamSpec);
392 streamSpec->SetStream(_stream);
393
394 CLimitedSequentialOutStream *outStreamSpec = new CLimitedSequentialOutStream;
395 CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
396
397 NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
398 CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
399
400 for (i = 0; i < numItems; i++)
401 {
402 lps->InSize = totalSize;
403 lps->OutSize = totalSize;
404 RINOK(lps->SetCur());
405 CMyComPtr<ISequentialOutStream> realOutStream;
406 Int32 askMode = testMode ?
407 NExtract::NAskMode::kTest :
408 NExtract::NAskMode::kExtract;
409 UInt32 index = allFilesMode ? i : indices[i];
410 const CParseItem &item = _items[index];
411
412 RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
413 UInt64 unpackSize = item.Size;
414 totalSize += unpackSize;
415 bool skipMode = false;
416 if (!testMode && !realOutStream)
417 continue;
418 RINOK(extractCallback->PrepareOperation(askMode));
419
420 outStreamSpec->SetStream(realOutStream);
421 realOutStream.Release();
422 outStreamSpec->Init(skipMode ? 0 : unpackSize, true);
423
424 Int32 opRes = NExtract::NOperationResult::kOK;
425 RINOK(_stream->Seek((Int64)item.Offset, STREAM_SEEK_SET, NULL));
426 streamSpec->Init(unpackSize);
427 RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress));
428
429 if (outStreamSpec->GetRem() != 0)
430 opRes = NExtract::NOperationResult::kDataError;
431 outStreamSpec->ReleaseStream();
432 RINOK(extractCallback->SetOperationResult(opRes));
433 }
434
435 return S_OK;
436
437 COM_TRY_END
438}
439
440
441STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
442{
443 COM_TRY_BEGIN
444 const CParseItem &item = _items[index];
445 return CreateLimitedInStream(_stream, item.Offset, item.Size, stream);
446 COM_TRY_END
447}
448
449}}
450
451#endif
452
453HRESULT Archive_GetItemBoolProp(IInArchive *arc, UInt32 index, PROPID propID, bool &result) throw()
454{
455 NCOM::CPropVariant prop;
456 result = false;
457 RINOK(arc->GetProperty(index, propID, &prop));
458 if (prop.vt == VT_BOOL)
459 result = VARIANT_BOOLToBool(prop.boolVal);
460 else if (prop.vt != VT_EMPTY)
461 return E_FAIL;
462 return S_OK;
463}
464
465HRESULT Archive_IsItem_Dir(IInArchive *arc, UInt32 index, bool &result) throw()
466{
467 return Archive_GetItemBoolProp(arc, index, kpidIsDir, result);
468}
469
470HRESULT Archive_IsItem_Aux(IInArchive *arc, UInt32 index, bool &result) throw()
471{
472 return Archive_GetItemBoolProp(arc, index, kpidIsAux, result);
473}
474
475HRESULT Archive_IsItem_AltStream(IInArchive *arc, UInt32 index, bool &result) throw()
476{
477 return Archive_GetItemBoolProp(arc, index, kpidIsAltStream, result);
478}
479
480HRESULT Archive_IsItem_Deleted(IInArchive *arc, UInt32 index, bool &result) throw()
481{
482 return Archive_GetItemBoolProp(arc, index, kpidIsDeleted, result);
483}
484
485static HRESULT Archive_GetArcBoolProp(IInArchive *arc, PROPID propid, bool &result) throw()
486{
487 NCOM::CPropVariant prop;
488 result = false;
489 RINOK(arc->GetArchiveProperty(propid, &prop));
490 if (prop.vt == VT_BOOL)
491 result = VARIANT_BOOLToBool(prop.boolVal);
492 else if (prop.vt != VT_EMPTY)
493 return E_FAIL;
494 return S_OK;
495}
496
497static HRESULT Archive_GetArcProp_UInt(IInArchive *arc, PROPID propid, UInt64 &result, bool &defined)
498{
499 defined = false;
500 NCOM::CPropVariant prop;
501 RINOK(arc->GetArchiveProperty(propid, &prop));
502 switch (prop.vt)
503 {
504 case VT_UI4: result = prop.ulVal; break;
505 case VT_I4: result = (UInt64)(Int64)prop.lVal; break;
506 case VT_UI8: result = (UInt64)prop.uhVal.QuadPart; break;
507 case VT_I8: result = (UInt64)prop.hVal.QuadPart; break;
508 case VT_EMPTY: return S_OK;
509 default: return E_FAIL;
510 }
511 defined = true;
512 return S_OK;
513}
514
515static HRESULT Archive_GetArcProp_Int(IInArchive *arc, PROPID propid, Int64 &result, bool &defined)
516{
517 defined = false;
518 NCOM::CPropVariant prop;
519 RINOK(arc->GetArchiveProperty(propid, &prop));
520 switch (prop.vt)
521 {
522 case VT_UI4: result = prop.ulVal; break;
523 case VT_I4: result = prop.lVal; break;
524 case VT_UI8: result = (Int64)prop.uhVal.QuadPart; break;
525 case VT_I8: result = (Int64)prop.hVal.QuadPart; break;
526 case VT_EMPTY: return S_OK;
527 default: return E_FAIL;
528 }
529 defined = true;
530 return S_OK;
531}
532
533#ifndef _SFX
534
535HRESULT CArc::GetItemPathToParent(UInt32 index, UInt32 parent, UStringVector &parts) const
536{
537 if (!GetRawProps)
538 return E_FAIL;
539 if (index == parent)
540 return S_OK;
541 UInt32 curIndex = index;
542
543 UString s;
544
545 bool prevWasAltStream = false;
546
547 for (;;)
548 {
549 #ifdef MY_CPU_LE
550 const void *p;
551 UInt32 size;
552 UInt32 propType;
553 RINOK(GetRawProps->GetRawProp(curIndex, kpidName, &p, &size, &propType));
554 if (p && propType == PROP_DATA_TYPE_wchar_t_PTR_Z_LE)
555 s = (const wchar_t *)p;
556 else
557 #endif
558 {
559 NCOM::CPropVariant prop;
560 RINOK(Archive->GetProperty(curIndex, kpidName, &prop));
561 if (prop.vt == VT_BSTR && prop.bstrVal)
562 s.SetFromBstr(prop.bstrVal);
563 else if (prop.vt == VT_EMPTY)
564 s.Empty();
565 else
566 return E_FAIL;
567 }
568
569 UInt32 curParent = (UInt32)(Int32)-1;
570 UInt32 parentType = 0;
571 RINOK(GetRawProps->GetParent(curIndex, &curParent, &parentType));
572
573 // 18.06: fixed : we don't want to split name to parts
574 /*
575 if (parentType != NParentType::kAltStream)
576 {
577 for (;;)
578 {
579 int pos = s.ReverseFind_PathSepar();
580 if (pos < 0)
581 {
582 break;
583 }
584 parts.Insert(0, s.Ptr(pos + 1));
585 s.DeleteFrom(pos);
586 }
587 }
588 */
589
590 parts.Insert(0, s);
591
592 if (prevWasAltStream)
593 {
594 {
595 UString &s2 = parts[parts.Size() - 2];
596 s2 += ':';
597 s2 += parts.Back();
598 }
599 parts.DeleteBack();
600 }
601
602 if (parent == curParent)
603 return S_OK;
604
605 prevWasAltStream = false;
606 if (parentType == NParentType::kAltStream)
607 prevWasAltStream = true;
608
609 if (curParent == (UInt32)(Int32)-1)
610 return E_FAIL;
611 curIndex = curParent;
612 }
613}
614
615#endif
616
617
618
619HRESULT CArc::GetItemPath(UInt32 index, UString &result) const
620{
621 #ifdef MY_CPU_LE
622 if (GetRawProps)
623 {
624 const void *p;
625 UInt32 size;
626 UInt32 propType;
627 if (!IsTree)
628 {
629 if (GetRawProps->GetRawProp(index, kpidPath, &p, &size, &propType) == S_OK &&
630 propType == NPropDataType::kUtf16z)
631 {
632 unsigned len = size / 2 - 1;
633 // (len) doesn't include null terminator
634
635 /*
636 #if WCHAR_MAX > 0xffff
637 len = (unsigned)Utf16LE__Get_Num_WCHARs(p, len);
638
639 wchar_t *s = result.GetBuf(len);
640 wchar_t *sEnd = Utf16LE__To_WCHARs_Sep(p, len, s);
641 if (s + len != sEnd) return E_FAIL;
642 *sEnd = 0;
643
644 #else
645 */
646
647 wchar_t *s = result.GetBuf(len);
648 for (unsigned i = 0; i < len; i++)
649 {
650 wchar_t c = GetUi16(p);
651 p = (const void *)((const Byte *)p + 2);
652
653 #if WCHAR_PATH_SEPARATOR != L'/'
654 if (c == L'/')
655 c = WCHAR_PATH_SEPARATOR;
656 else if (c == L'\\')
657 c = WCHAR_IN_FILE_NAME_BACKSLASH_REPLACEMENT; // WSL scheme
658 #endif
659
660 *s++ = c;
661 }
662 *s = 0;
663
664 // #endif
665
666 result.ReleaseBuf_SetLen(len);
667
668 Convert_UnicodeEsc16_To_UnicodeEscHigh(result);
669 if (len != 0)
670 return S_OK;
671 }
672 }
673 /*
674 else if (GetRawProps->GetRawProp(index, kpidName, &p, &size, &propType) == S_OK &&
675 p && propType == NPropDataType::kUtf16z)
676 {
677 size -= 2;
678 UInt32 totalSize = size;
679 bool isOK = false;
680
681 {
682 UInt32 index2 = index;
683 for (;;)
684 {
685 UInt32 parent = (UInt32)(Int32)-1;
686 UInt32 parentType = 0;
687 if (GetRawProps->GetParent(index2, &parent, &parentType) != S_OK)
688 break;
689 if (parent == (UInt32)(Int32)-1)
690 {
691 if (parentType != 0)
692 totalSize += 2;
693 isOK = true;
694 break;
695 }
696 index2 = parent;
697 UInt32 size2;
698 const void *p2;
699 if (GetRawProps->GetRawProp(index2, kpidName, &p2, &size2, &propType) != S_OK &&
700 p2 && propType == NPropDataType::kUtf16z)
701 break;
702 totalSize += size2;
703 }
704 }
705
706 if (isOK)
707 {
708 wchar_t *sz = result.GetBuf_SetEnd(totalSize / 2);
709 UInt32 pos = totalSize - size;
710 memcpy((Byte *)sz + pos, p, size);
711 UInt32 index2 = index;
712 for (;;)
713 {
714 UInt32 parent = (UInt32)(Int32)-1;
715 UInt32 parentType = 0;
716 if (GetRawProps->GetParent(index2, &parent, &parentType) != S_OK)
717 break;
718 if (parent == (UInt32)(Int32)-1)
719 {
720 if (parentType != 0)
721 sz[pos / 2 - 1] = L':';
722 break;
723 }
724 index2 = parent;
725 UInt32 size2;
726 const void *p2;
727 if (GetRawProps->GetRawProp(index2, kpidName, &p2, &size2, &propType) != S_OK)
728 break;
729 pos -= size2;
730 memcpy((Byte *)sz + pos, p2, size2);
731 sz[(pos + size2 - 2) / 2] = (parentType == 0) ? WCHAR_PATH_SEPARATOR : L':';
732 }
733 #ifdef _WIN32
734 // result.Replace(L'/', WCHAR_PATH_SEPARATOR);
735 #endif
736 return S_OK;
737 }
738 }
739 */
740 }
741 #endif
742
743 {
744 NCOM::CPropVariant prop;
745 RINOK(Archive->GetProperty(index, kpidPath, &prop));
746 if (prop.vt == VT_BSTR && prop.bstrVal)
747 result.SetFromBstr(prop.bstrVal);
748 else if (prop.vt == VT_EMPTY)
749 result.Empty();
750 else
751 return E_FAIL;
752 }
753
754 if (result.IsEmpty())
755 return GetDefaultItemPath(index, result);
756
757 Convert_UnicodeEsc16_To_UnicodeEscHigh(result);
758 return S_OK;
759}
760
761HRESULT CArc::GetDefaultItemPath(UInt32 index, UString &result) const
762{
763 result.Empty();
764 bool isDir;
765 RINOK(Archive_IsItem_Dir(Archive, index, isDir));
766 if (!isDir)
767 {
768 result = DefaultName;
769 NCOM::CPropVariant prop;
770 RINOK(Archive->GetProperty(index, kpidExtension, &prop));
771 if (prop.vt == VT_BSTR)
772 {
773 result += '.';
774 result += prop.bstrVal;
775 }
776 else if (prop.vt != VT_EMPTY)
777 return E_FAIL;
778 }
779 return S_OK;
780}
781
782HRESULT CArc::GetItemPath2(UInt32 index, UString &result) const
783{
784 RINOK(GetItemPath(index, result));
785 if (Ask_Deleted)
786 {
787 bool isDeleted = false;
788 RINOK(Archive_IsItem_Deleted(Archive, index, isDeleted));
789 if (isDeleted)
790 result.Insert(0, L"[DELETED]" WSTRING_PATH_SEPARATOR);
791 }
792 return S_OK;
793}
794
795#ifdef SUPPORT_ALT_STREAMS
796
797int FindAltStreamColon_in_Path(const wchar_t *path)
798{
799 unsigned i = 0;
800 int colonPos = -1;
801 for (;; i++)
802 {
803 wchar_t c = path[i];
804 if (c == 0)
805 return colonPos;
806 if (c == ':')
807 {
808 if (colonPos < 0)
809 colonPos = (int)i;
810 continue;
811 }
812 if (c == WCHAR_PATH_SEPARATOR)
813 colonPos = -1;
814 }
815}
816
817#endif
818
819HRESULT CArc::GetItem(UInt32 index, CReadArcItem &item) const
820{
821 #ifdef SUPPORT_ALT_STREAMS
822 item.IsAltStream = false;
823 item.AltStreamName.Empty();
824 item.MainPath.Empty();
825 #endif
826
827 item.IsDir = false;
828 item.Path.Empty();
829 item.ParentIndex = (UInt32)(Int32)-1;
830
831 item.PathParts.Clear();
832
833 RINOK(Archive_IsItem_Dir(Archive, index, item.IsDir));
834 item.MainIsDir = item.IsDir;
835
836 RINOK(GetItemPath2(index, item.Path));
837
838 #ifndef _SFX
839 UInt32 mainIndex = index;
840 #endif
841
842 #ifdef SUPPORT_ALT_STREAMS
843
844 item.MainPath = item.Path;
845 if (Ask_AltStream)
846 {
847 RINOK(Archive_IsItem_AltStream(Archive, index, item.IsAltStream));
848 }
849
850 bool needFindAltStream = false;
851
852 if (item.IsAltStream)
853 {
854 needFindAltStream = true;
855 if (GetRawProps)
856 {
857 UInt32 parentType = 0;
858 UInt32 parentIndex;
859 RINOK(GetRawProps->GetParent(index, &parentIndex, &parentType));
860 if (parentType == NParentType::kAltStream)
861 {
862 NCOM::CPropVariant prop;
863 RINOK(Archive->GetProperty(index, kpidName, &prop));
864 if (prop.vt == VT_BSTR && prop.bstrVal)
865 item.AltStreamName.SetFromBstr(prop.bstrVal);
866 else if (prop.vt != VT_EMPTY)
867 return E_FAIL;
868 else
869 {
870 // item.IsAltStream = false;
871 }
872 /*
873 if (item.AltStreamName.IsEmpty())
874 item.IsAltStream = false;
875 */
876
877 needFindAltStream = false;
878 item.ParentIndex = parentIndex;
879 mainIndex = parentIndex;
880
881 if (parentIndex == (UInt32)(Int32)-1)
882 {
883 item.MainPath.Empty();
884 item.MainIsDir = true;
885 }
886 else
887 {
888 RINOK(GetItemPath2(parentIndex, item.MainPath));
889 RINOK(Archive_IsItem_Dir(Archive, parentIndex, item.MainIsDir));
890 }
891 }
892 }
893 }
894
895 if (item.WriteToAltStreamIfColon || needFindAltStream)
896 {
897 /* Good handler must support GetRawProps::GetParent for alt streams.
898 So the following code currently is not used */
899 int colon = FindAltStreamColon_in_Path(item.Path);
900 if (colon >= 0)
901 {
902 item.MainPath.DeleteFrom((unsigned)colon);
903 item.AltStreamName = item.Path.Ptr((unsigned)(colon + 1));
904 item.MainIsDir = (colon == 0 || IsPathSepar(item.Path[(unsigned)colon - 1]));
905 item.IsAltStream = true;
906 }
907 }
908
909 #endif
910
911 #ifndef _SFX
912 if (item._use_baseParentFolder_mode)
913 {
914 RINOK(GetItemPathToParent(mainIndex, (unsigned)item._baseParentFolder, item.PathParts));
915
916 #ifdef SUPPORT_ALT_STREAMS
917 if ((item.WriteToAltStreamIfColon || needFindAltStream) && !item.PathParts.IsEmpty())
918 {
919 int colon;
920 {
921 UString &s = item.PathParts.Back();
922 colon = FindAltStreamColon_in_Path(s);
923 if (colon >= 0)
924 {
925 item.AltStreamName = s.Ptr((unsigned)(colon + 1));
926 item.MainIsDir = (colon == 0 || IsPathSepar(s[(unsigned)colon - 1]));
927 item.IsAltStream = true;
928 s.DeleteFrom((unsigned)colon);
929 }
930 }
931 if (colon == 0)
932 item.PathParts.DeleteBack();
933 }
934 #endif
935
936 }
937 else
938 #endif
939 SplitPathToParts(
940 #ifdef SUPPORT_ALT_STREAMS
941 item.MainPath
942 #else
943 item.Path
944 #endif
945 , item.PathParts);
946
947 return S_OK;
948}
949
950#ifndef _SFX
951
952static HRESULT Archive_GetItem_Size(IInArchive *archive, UInt32 index, UInt64 &size, bool &defined)
953{
954 NCOM::CPropVariant prop;
955 defined = false;
956 size = 0;
957 RINOK(archive->GetProperty(index, kpidSize, &prop));
958 switch (prop.vt)
959 {
960 case VT_UI1: size = prop.bVal; break;
961 case VT_UI2: size = prop.uiVal; break;
962 case VT_UI4: size = prop.ulVal; break;
963 case VT_UI8: size = (UInt64)prop.uhVal.QuadPart; break;
964 case VT_EMPTY: return S_OK;
965 default: return E_FAIL;
966 }
967 defined = true;
968 return S_OK;
969}
970
971#endif
972
973HRESULT CArc::GetItemSize(UInt32 index, UInt64 &size, bool &defined) const
974{
975 NCOM::CPropVariant prop;
976 defined = false;
977 size = 0;
978 RINOK(Archive->GetProperty(index, kpidSize, &prop));
979 switch (prop.vt)
980 {
981 case VT_UI1: size = prop.bVal; break;
982 case VT_UI2: size = prop.uiVal; break;
983 case VT_UI4: size = prop.ulVal; break;
984 case VT_UI8: size = (UInt64)prop.uhVal.QuadPart; break;
985 case VT_EMPTY: return S_OK;
986 default: return E_FAIL;
987 }
988 defined = true;
989 return S_OK;
990}
991
992HRESULT CArc::GetItemMTime(UInt32 index, FILETIME &ft, bool &defined) const
993{
994 NCOM::CPropVariant prop;
995 defined = false;
996 ft.dwHighDateTime = ft.dwLowDateTime = 0;
997 RINOK(Archive->GetProperty(index, kpidMTime, &prop));
998 if (prop.vt == VT_FILETIME)
999 {
1000 ft = prop.filetime;
1001 defined = true;
1002 }
1003 else if (prop.vt != VT_EMPTY)
1004 return E_FAIL;
1005 else if (MTimeDefined)
1006 {
1007 ft = MTime;
1008 defined = true;
1009 }
1010 return S_OK;
1011}
1012
1013#ifndef _SFX
1014
1015static inline bool TestSignature(const Byte *p1, const Byte *p2, size_t size)
1016{
1017 for (size_t i = 0; i < size; i++)
1018 if (p1[i] != p2[i])
1019 return false;
1020 return true;
1021}
1022
1023static void MakeCheckOrder(CCodecs *codecs,
1024 CIntVector &orderIndices, unsigned numTypes, CIntVector &orderIndices2,
1025 const Byte *data, size_t dataSize)
1026{
1027 for (unsigned i = 0; i < numTypes; i++)
1028 {
1029 const int index = orderIndices[i];
1030 if (index < 0)
1031 continue;
1032 const CArcInfoEx &ai = codecs->Formats[(unsigned)index];
1033 if (ai.SignatureOffset == 0)
1034 {
1035 if (ai.Signatures.IsEmpty())
1036 {
1037 if (dataSize != 0) // 21.04: no Sinature means Empty Signature
1038 continue;
1039 }
1040 else
1041 {
1042 unsigned k;
1043 const CObjectVector<CByteBuffer> &sigs = ai.Signatures;
1044 for (k = 0; k < sigs.Size(); k++)
1045 {
1046 const CByteBuffer &sig = sigs[k];
1047 if (sig.Size() <= dataSize && TestSignature(data, sig, sig.Size()))
1048 break;
1049 }
1050 if (k == sigs.Size())
1051 continue;
1052 }
1053 }
1054 orderIndices2.Add(index);
1055 orderIndices[i] = -1;
1056 }
1057}
1058
1059#ifdef UNDER_CE
1060 static const unsigned kNumHashBytes = 1;
1061 #define HASH_VAL(buf) ((buf)[0])
1062#else
1063 static const unsigned kNumHashBytes = 2;
1064 // #define HASH_VAL(buf) ((buf)[0] | ((UInt32)(buf)[1] << 8))
1065 #define HASH_VAL(buf) GetUi16(buf)
1066#endif
1067
1068static bool IsExeExt(const UString &ext)
1069{
1070 return ext.IsEqualTo_Ascii_NoCase("exe");
1071}
1072
1073static const char * const k_PreArcFormats[] =
1074{
1075 "pe"
1076 , "elf"
1077 , "macho"
1078 , "mub"
1079 , "te"
1080};
1081
1082static bool IsNameFromList(const UString &s, const char * const names[], size_t num)
1083{
1084 for (unsigned i = 0; i < num; i++)
1085 if (StringsAreEqualNoCase_Ascii(s, names[i]))
1086 return true;
1087 return false;
1088}
1089
1090
1091static bool IsPreArcFormat(const CArcInfoEx &ai)
1092{
1093 if (ai.Flags_PreArc())
1094 return true;
1095 return IsNameFromList(ai.Name, k_PreArcFormats, ARRAY_SIZE(k_PreArcFormats));
1096}
1097
1098static const char * const k_Formats_with_simple_signuature[] =
1099{
1100 "7z"
1101 , "xz"
1102 , "rar"
1103 , "bzip2"
1104 , "gzip"
1105 , "cab"
1106 , "wim"
1107 , "rpm"
1108 , "vhd"
1109 , "xar"
1110};
1111
1112static bool IsNewStyleSignature(const CArcInfoEx &ai)
1113{
1114 // if (ai.Version >= 0x91F)
1115 if (ai.NewInterface)
1116 return true;
1117 return IsNameFromList(ai.Name, k_Formats_with_simple_signuature, ARRAY_SIZE(k_Formats_with_simple_signuature));
1118}
1119
1120class CArchiveOpenCallback_Offset:
1121 public IArchiveOpenCallback,
1122 public IArchiveOpenVolumeCallback,
1123 #ifndef _NO_CRYPTO
1124 public ICryptoGetTextPassword,
1125 #endif
1126 public CMyUnknownImp
1127{
1128public:
1129 CMyComPtr<IArchiveOpenCallback> Callback;
1130 CMyComPtr<IArchiveOpenVolumeCallback> OpenVolumeCallback;
1131 UInt64 Files;
1132 UInt64 Offset;
1133
1134 #ifndef _NO_CRYPTO
1135 CMyComPtr<ICryptoGetTextPassword> GetTextPassword;
1136 #endif
1137
1138 MY_QUERYINTERFACE_BEGIN2(IArchiveOpenCallback)
1139 MY_QUERYINTERFACE_ENTRY(IArchiveOpenVolumeCallback)
1140 #ifndef _NO_CRYPTO
1141 MY_QUERYINTERFACE_ENTRY(ICryptoGetTextPassword)
1142 #endif
1143 MY_QUERYINTERFACE_END
1144 MY_ADDREF_RELEASE
1145
1146 INTERFACE_IArchiveOpenCallback(;)
1147 INTERFACE_IArchiveOpenVolumeCallback(;)
1148 #ifndef _NO_CRYPTO
1149 STDMETHOD(CryptoGetTextPassword)(BSTR *password);
1150 #endif
1151};
1152
1153#ifndef _NO_CRYPTO
1154STDMETHODIMP CArchiveOpenCallback_Offset::CryptoGetTextPassword(BSTR *password)
1155{
1156 COM_TRY_BEGIN
1157 if (GetTextPassword)
1158 return GetTextPassword->CryptoGetTextPassword(password);
1159 return E_NOTIMPL;
1160 COM_TRY_END
1161}
1162#endif
1163
1164STDMETHODIMP CArchiveOpenCallback_Offset::SetTotal(const UInt64 *, const UInt64 *)
1165{
1166 return S_OK;
1167}
1168
1169STDMETHODIMP CArchiveOpenCallback_Offset::SetCompleted(const UInt64 *, const UInt64 *bytes)
1170{
1171 if (!Callback)
1172 return S_OK;
1173 UInt64 value = Offset;
1174 if (bytes)
1175 value += *bytes;
1176 return Callback->SetCompleted(&Files, &value);
1177}
1178
1179STDMETHODIMP CArchiveOpenCallback_Offset::GetProperty(PROPID propID, PROPVARIANT *value)
1180{
1181 if (OpenVolumeCallback)
1182 return OpenVolumeCallback->GetProperty(propID, value);
1183 NCOM::PropVariant_Clear(value);
1184 return S_OK;
1185 // return E_NOTIMPL;
1186}
1187
1188STDMETHODIMP CArchiveOpenCallback_Offset::GetStream(const wchar_t *name, IInStream **inStream)
1189{
1190 if (OpenVolumeCallback)
1191 return OpenVolumeCallback->GetStream(name, inStream);
1192 return S_FALSE;
1193}
1194
1195#endif
1196
1197
1198UInt32 GetOpenArcErrorFlags(const NCOM::CPropVariant &prop, bool *isDefinedProp)
1199{
1200 if (isDefinedProp != NULL)
1201 *isDefinedProp = false;
1202
1203 switch (prop.vt)
1204 {
1205 case VT_UI8: if (isDefinedProp) *isDefinedProp = true; return (UInt32)prop.uhVal.QuadPart;
1206 case VT_UI4: if (isDefinedProp) *isDefinedProp = true; return prop.ulVal;
1207 case VT_EMPTY: return 0;
1208 default: throw 151199;
1209 }
1210}
1211
1212void CArcErrorInfo::ClearErrors()
1213{
1214 // ErrorFormatIndex = -1; // we don't need to clear ErrorFormatIndex here !!!
1215
1216 ThereIsTail = false;
1217 UnexpecedEnd = false;
1218 IgnoreTail = false;
1219 // NonZerosTail = false;
1220 ErrorFlags_Defined = false;
1221 ErrorFlags = 0;
1222 WarningFlags = 0;
1223 TailSize = 0;
1224
1225 ErrorMessage.Empty();
1226 WarningMessage.Empty();
1227}
1228
1229HRESULT CArc::ReadBasicProps(IInArchive *archive, UInt64 startPos, HRESULT openRes)
1230{
1231 // OkPhySize_Defined = false;
1232 PhySizeDefined = false;
1233 PhySize = 0;
1234 Offset = 0;
1235 AvailPhySize = FileSize - startPos;
1236
1237 ErrorInfo.ClearErrors();
1238 {
1239 NCOM::CPropVariant prop;
1240 RINOK(archive->GetArchiveProperty(kpidErrorFlags, &prop));
1241 ErrorInfo.ErrorFlags = GetOpenArcErrorFlags(prop, &ErrorInfo.ErrorFlags_Defined);
1242 }
1243 {
1244 NCOM::CPropVariant prop;
1245 RINOK(archive->GetArchiveProperty(kpidWarningFlags, &prop));
1246 ErrorInfo.WarningFlags = GetOpenArcErrorFlags(prop);
1247 }
1248
1249 {
1250 NCOM::CPropVariant prop;
1251 RINOK(archive->GetArchiveProperty(kpidError, &prop));
1252 if (prop.vt != VT_EMPTY)
1253 ErrorInfo.ErrorMessage = (prop.vt == VT_BSTR ? prop.bstrVal : L"Unknown error");
1254 }
1255
1256 {
1257 NCOM::CPropVariant prop;
1258 RINOK(archive->GetArchiveProperty(kpidWarning, &prop));
1259 if (prop.vt != VT_EMPTY)
1260 ErrorInfo.WarningMessage = (prop.vt == VT_BSTR ? prop.bstrVal : L"Unknown warning");
1261 }
1262
1263 if (openRes == S_OK || ErrorInfo.IsArc_After_NonOpen())
1264 {
1265 RINOK(Archive_GetArcProp_UInt(archive, kpidPhySize, PhySize, PhySizeDefined));
1266 /*
1267 RINOK(Archive_GetArcProp_UInt(archive, kpidOkPhySize, OkPhySize, OkPhySize_Defined));
1268 if (!OkPhySize_Defined)
1269 {
1270 OkPhySize_Defined = PhySizeDefined;
1271 OkPhySize = PhySize;
1272 }
1273 */
1274
1275 bool offsetDefined;
1276 RINOK(Archive_GetArcProp_Int(archive, kpidOffset, Offset, offsetDefined));
1277
1278 Int64 globalOffset = (Int64)startPos + Offset;
1279 AvailPhySize = (UInt64)((Int64)FileSize - globalOffset);
1280 if (PhySizeDefined)
1281 {
1282 UInt64 endPos = (UInt64)(globalOffset + (Int64)PhySize);
1283 if (endPos < FileSize)
1284 {
1285 AvailPhySize = PhySize;
1286 ErrorInfo.ThereIsTail = true;
1287 ErrorInfo.TailSize = FileSize - endPos;
1288 }
1289 else if (endPos > FileSize)
1290 ErrorInfo.UnexpecedEnd = true;
1291 }
1292 }
1293
1294 return S_OK;
1295}
1296
1297/*
1298static void PrintNumber(const char *s, int n)
1299{
1300 char temp[100];
1301 sprintf(temp, "%s %d", s, n);
1302 // OutputDebugStringA(temp);
1303 printf(temp);
1304}
1305*/
1306
1307HRESULT CArc::PrepareToOpen(const COpenOptions &op, unsigned formatIndex, CMyComPtr<IInArchive> &archive)
1308{
1309 // OutputDebugStringA("a1");
1310 // PrintNumber("formatIndex", formatIndex);
1311
1312 RINOK(op.codecs->CreateInArchive(formatIndex, archive));
1313 // OutputDebugStringA("a2");
1314 if (!archive)
1315 return S_OK;
1316
1317 #ifdef EXTERNAL_CODECS
1318 if (op.codecs->NeedSetLibCodecs)
1319 {
1320 const CArcInfoEx &ai = op.codecs->Formats[formatIndex];
1321 if (ai.LibIndex >= 0 ?
1322 !op.codecs->Libs[(unsigned)ai.LibIndex].SetCodecs :
1323 !op.codecs->Libs.IsEmpty())
1324 {
1325 CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
1326 archive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
1327 if (setCompressCodecsInfo)
1328 {
1329 RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(op.codecs));
1330 }
1331 }
1332 }
1333 #endif
1334
1335
1336 #ifndef _SFX
1337
1338 const CArcInfoEx &ai = op.codecs->Formats[formatIndex];
1339
1340 // OutputDebugStringW(ai.Name);
1341 // OutputDebugStringA("a3");
1342
1343 if (ai.Flags_PreArc())
1344 {
1345 /* we notify parsers that extract executables, that they don't need
1346 to open archive, if there is tail after executable (for SFX cases) */
1347 CMyComPtr<IArchiveAllowTail> allowTail;
1348 archive.QueryInterface(IID_IArchiveAllowTail, (void **)&allowTail);
1349 if (allowTail)
1350 allowTail->AllowTail(BoolToInt(true));
1351 }
1352
1353 if (op.props)
1354 {
1355 /*
1356 FOR_VECTOR (y, op.props)
1357 {
1358 const COptionalOpenProperties &optProps = (*op.props)[y];
1359 if (optProps.FormatName.IsEmpty() || optProps.FormatName.CompareNoCase(ai.Name) == 0)
1360 {
1361 RINOK(SetProperties(archive, optProps.Props));
1362 break;
1363 }
1364 }
1365 */
1366 RINOK(SetProperties(archive, *op.props));
1367 }
1368
1369 #endif
1370 return S_OK;
1371}
1372
1373#ifndef _SFX
1374
1375static HRESULT ReadParseItemProps(IInArchive *archive, const CArcInfoEx &ai, NArchive::NParser::CParseItem &pi)
1376{
1377 pi.Extension = ai.GetMainExt();
1378 pi.FileTime_Defined = false;
1379 pi.ArcType = ai.Name;
1380
1381 RINOK(Archive_GetArcBoolProp(archive, kpidIsNotArcType, pi.IsNotArcType));
1382
1383 // RINOK(Archive_GetArcBoolProp(archive, kpidIsSelfExe, pi.IsSelfExe));
1384 pi.IsSelfExe = ai.Flags_PreArc();
1385
1386 {
1387 NCOM::CPropVariant prop;
1388 RINOK(archive->GetArchiveProperty(kpidMTime, &prop));
1389 if (prop.vt == VT_FILETIME)
1390 {
1391 pi.FileTime_Defined = true;
1392 pi.FileTime = prop.filetime;
1393 }
1394 }
1395
1396 if (!pi.FileTime_Defined)
1397 {
1398 NCOM::CPropVariant prop;
1399 RINOK(archive->GetArchiveProperty(kpidCTime, &prop));
1400 if (prop.vt == VT_FILETIME)
1401 {
1402 pi.FileTime_Defined = true;
1403 pi.FileTime = prop.filetime;
1404 }
1405 }
1406
1407 {
1408 NCOM::CPropVariant prop;
1409 RINOK(archive->GetArchiveProperty(kpidName, &prop));
1410 if (prop.vt == VT_BSTR)
1411 {
1412 pi.Name.SetFromBstr(prop.bstrVal);
1413 pi.Extension.Empty();
1414 }
1415 else
1416 {
1417 RINOK(archive->GetArchiveProperty(kpidExtension, &prop));
1418 if (prop.vt == VT_BSTR)
1419 pi.Extension.SetFromBstr(prop.bstrVal);
1420 }
1421 }
1422
1423 {
1424 NCOM::CPropVariant prop;
1425 RINOK(archive->GetArchiveProperty(kpidShortComment, &prop));
1426 if (prop.vt == VT_BSTR)
1427 pi.Comment.SetFromBstr(prop.bstrVal);
1428 }
1429
1430
1431 UInt32 numItems;
1432 RINOK(archive->GetNumberOfItems(&numItems));
1433
1434 // pi.NumSubFiles = numItems;
1435 // RINOK(Archive_GetArcProp_UInt(archive, kpidUnpackSize, pi.UnpackSize, pi.UnpackSize_Defined));
1436 // if (!pi.UnpackSize_Defined)
1437 {
1438 pi.NumSubFiles = 0;
1439 pi.NumSubDirs = 0;
1440 pi.UnpackSize = 0;
1441 for (UInt32 i = 0; i < numItems; i++)
1442 {
1443 UInt64 size = 0;
1444 bool defined = false;
1445 Archive_GetItem_Size(archive, i, size, defined);
1446 if (defined)
1447 {
1448 pi.UnpackSize_Defined = true;
1449 pi.UnpackSize += size;
1450 }
1451
1452 bool isDir = false;
1453 Archive_IsItem_Dir(archive, i, isDir);
1454 if (isDir)
1455 pi.NumSubDirs++;
1456 else
1457 pi.NumSubFiles++;
1458 }
1459 if (pi.NumSubDirs != 0)
1460 pi.NumSubDirs_Defined = true;
1461 pi.NumSubFiles_Defined = true;
1462 }
1463
1464 return S_OK;
1465}
1466
1467#endif
1468
1469HRESULT CArc::CheckZerosTail(const COpenOptions &op, UInt64 offset)
1470{
1471 if (!op.stream)
1472 return S_OK;
1473 RINOK(op.stream->Seek((Int64)offset, STREAM_SEEK_SET, NULL));
1474 const UInt32 kBufSize = 1 << 11;
1475 Byte buf[kBufSize];
1476
1477 for (;;)
1478 {
1479 UInt32 processed = 0;
1480 RINOK(op.stream->Read(buf, kBufSize, &processed));
1481 if (processed == 0)
1482 {
1483 // ErrorInfo.NonZerosTail = false;
1484 ErrorInfo.IgnoreTail = true;
1485 return S_OK;
1486 }
1487 for (size_t i = 0; i < processed; i++)
1488 {
1489 if (buf[i] != 0)
1490 {
1491 // ErrorInfo.IgnoreTail = false;
1492 // ErrorInfo.NonZerosTail = true;
1493 return S_OK;
1494 }
1495 }
1496 }
1497}
1498
1499
1500
1501#ifndef _SFX
1502
1503class CExtractCallback_To_OpenCallback:
1504 public IArchiveExtractCallback,
1505 public ICompressProgressInfo,
1506 public CMyUnknownImp
1507{
1508public:
1509 CMyComPtr<IArchiveOpenCallback> Callback;
1510 UInt64 Files;
1511 UInt64 Offset;
1512
1513 MY_UNKNOWN_IMP2(IArchiveExtractCallback, ICompressProgressInfo)
1514 INTERFACE_IArchiveExtractCallback(;)
1515 STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize);
1516 void Init(IArchiveOpenCallback *callback)
1517 {
1518 Callback = callback;
1519 Files = 0;
1520 Offset = 0;
1521 }
1522};
1523
1524STDMETHODIMP CExtractCallback_To_OpenCallback::SetTotal(UInt64 /* size */)
1525{
1526 return S_OK;
1527}
1528
1529STDMETHODIMP CExtractCallback_To_OpenCallback::SetCompleted(const UInt64 * /* completeValue */)
1530{
1531 return S_OK;
1532}
1533
1534STDMETHODIMP CExtractCallback_To_OpenCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 * /* outSize */)
1535{
1536 if (Callback)
1537 {
1538 UInt64 value = Offset;
1539 if (inSize)
1540 value += *inSize;
1541 return Callback->SetCompleted(&Files, &value);
1542 }
1543 return S_OK;
1544}
1545
1546STDMETHODIMP CExtractCallback_To_OpenCallback::GetStream(UInt32 /* index */, ISequentialOutStream **outStream, Int32 /* askExtractMode */)
1547{
1548 *outStream = NULL;
1549 return S_OK;
1550}
1551
1552STDMETHODIMP CExtractCallback_To_OpenCallback::PrepareOperation(Int32 /* askExtractMode */)
1553{
1554 return S_OK;
1555}
1556
1557STDMETHODIMP CExtractCallback_To_OpenCallback::SetOperationResult(Int32 /* operationResult */)
1558{
1559 return S_OK;
1560}
1561
1562
1563static HRESULT OpenArchiveSpec(IInArchive *archive, bool needPhySize,
1564 IInStream *stream, const UInt64 *maxCheckStartPosition,
1565 IArchiveOpenCallback *openCallback,
1566 IArchiveExtractCallback *extractCallback)
1567{
1568 /*
1569 if (needPhySize)
1570 {
1571 CMyComPtr<IArchiveOpen2> open2;
1572 archive->QueryInterface(IID_IArchiveOpen2, (void **)&open2);
1573 if (open2)
1574 return open2->ArcOpen2(stream, kOpenFlags_RealPhySize, openCallback);
1575 }
1576 */
1577 RINOK(archive->Open(stream, maxCheckStartPosition, openCallback));
1578 if (needPhySize)
1579 {
1580 bool phySize_Defined = false;
1581 UInt64 phySize = 0;
1582 RINOK(Archive_GetArcProp_UInt(archive, kpidPhySize, phySize, phySize_Defined));
1583 if (phySize_Defined)
1584 return S_OK;
1585
1586 bool phySizeCantBeDetected = false;
1587 RINOK(Archive_GetArcBoolProp(archive, kpidPhySizeCantBeDetected, phySizeCantBeDetected));
1588
1589 if (!phySizeCantBeDetected)
1590 {
1591 PRF(printf("\n-- !phySize_Defined after Open, call archive->Extract()"));
1592 // It's for bzip2/gz and some xz archives, where Open operation doesn't know phySize.
1593 // But the Handler will know phySize after full archive testing.
1594 RINOK(archive->Extract(NULL, (UInt32)(Int32)-1, BoolToInt(true), extractCallback));
1595 PRF(printf("\n-- OK"));
1596 }
1597 }
1598 return S_OK;
1599}
1600
1601
1602
1603static int FindFormatForArchiveType(CCodecs *codecs, CIntVector orderIndices, const char *name)
1604{
1605 FOR_VECTOR (i, orderIndices)
1606 {
1607 int oi = orderIndices[i];
1608 if (oi >= 0)
1609 if (StringsAreEqualNoCase_Ascii(codecs->Formats[(unsigned)oi].Name, name))
1610 return (int)i;
1611 }
1612 return -1;
1613}
1614
1615#endif
1616
1617HRESULT CArc::OpenStream2(const COpenOptions &op)
1618{
1619 // fprintf(stdout, "\nOpen: %S", Path); fflush(stdout);
1620
1621 Archive.Release();
1622 GetRawProps.Release();
1623 GetRootProps.Release();
1624
1625 ErrorInfo.ClearErrors();
1626 ErrorInfo.ErrorFormatIndex = -1;
1627
1628 IsParseArc = false;
1629 ArcStreamOffset = 0;
1630
1631 // OutputDebugStringA("1");
1632 // OutputDebugStringW(Path);
1633
1634 const UString fileName = ExtractFileNameFromPath(Path);
1635 UString extension;
1636 {
1637 int dotPos = fileName.ReverseFind_Dot();
1638 if (dotPos >= 0)
1639 extension = fileName.Ptr((unsigned)(dotPos + 1));
1640 }
1641
1642 CIntVector orderIndices;
1643
1644 bool searchMarkerInHandler = false;
1645 #ifdef _SFX
1646 searchMarkerInHandler = true;
1647 #endif
1648
1649 CBoolArr isMainFormatArr(op.codecs->Formats.Size());
1650 {
1651 FOR_VECTOR(i, op.codecs->Formats)
1652 isMainFormatArr[i] = false;
1653 }
1654
1655 UInt64 maxStartOffset =
1656 op.openType.MaxStartOffset_Defined ?
1657 op.openType.MaxStartOffset :
1658 kMaxCheckStartPosition;
1659
1660 #ifndef _SFX
1661 bool isUnknownExt = false;
1662 #endif
1663
1664 #ifndef _SFX
1665 bool isForced = false;
1666 #endif
1667
1668 unsigned numMainTypes = 0;
1669 int formatIndex = op.openType.FormatIndex;
1670
1671 if (formatIndex >= 0)
1672 {
1673 #ifndef _SFX
1674 isForced = true;
1675 #endif
1676 orderIndices.Add(formatIndex);
1677 numMainTypes = 1;
1678 isMainFormatArr[(unsigned)formatIndex] = true;
1679
1680 searchMarkerInHandler = true;
1681 }
1682 else
1683 {
1684 unsigned numFinded = 0;
1685 #ifndef _SFX
1686 bool isPrearcExt = false;
1687 #endif
1688
1689 {
1690 #ifndef _SFX
1691
1692 bool isZip = false;
1693 bool isRar = false;
1694
1695 const wchar_t c = extension[0];
1696 if (c == 'z' || c == 'Z' || c == 'r' || c == 'R')
1697 {
1698 bool isNumber = false;
1699 for (unsigned k = 1;; k++)
1700 {
1701 const wchar_t d = extension[k];
1702 if (d == 0)
1703 break;
1704 if (d < '0' || d > '9')
1705 {
1706 isNumber = false;
1707 break;
1708 }
1709 isNumber = true;
1710 }
1711 if (isNumber)
1712 {
1713 if (c == 'z' || c == 'Z')
1714 isZip = true;
1715 else
1716 isRar = true;
1717 }
1718 }
1719
1720 #endif
1721
1722 FOR_VECTOR (i, op.codecs->Formats)
1723 {
1724 const CArcInfoEx &ai = op.codecs->Formats[i];
1725
1726 if (IgnoreSplit || !op.openType.CanReturnArc)
1727 if (ai.IsSplit())
1728 continue;
1729 if (op.excludedFormats->FindInSorted((int)i) >= 0)
1730 continue;
1731
1732 #ifndef _SFX
1733 if (IsPreArcFormat(ai))
1734 isPrearcExt = true;
1735 #endif
1736
1737 if (ai.FindExtension(extension) >= 0
1738 #ifndef _SFX
1739 || (isZip && StringsAreEqualNoCase_Ascii(ai.Name, "zip"))
1740 || (isRar && StringsAreEqualNoCase_Ascii(ai.Name, "rar"))
1741 #endif
1742 )
1743 {
1744 // PrintNumber("orderIndices.Insert", i);
1745 orderIndices.Insert(numFinded++, (int)i);
1746 isMainFormatArr[i] = true;
1747 }
1748 else
1749 orderIndices.Add((int)i);
1750 }
1751 }
1752
1753 if (!op.stream)
1754 {
1755 if (numFinded != 1)
1756 return E_NOTIMPL;
1757 orderIndices.DeleteFrom(1);
1758 }
1759 // PrintNumber("numFinded", numFinded );
1760
1761 /*
1762 if (op.openOnlySpecifiedByExtension)
1763 {
1764 if (numFinded != 0 && !IsExeExt(extension))
1765 orderIndices.DeleteFrom(numFinded);
1766 }
1767 */
1768
1769 #ifndef _SFX
1770
1771 if (op.stream && orderIndices.Size() >= 2)
1772 {
1773 RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL));
1774 CByteBuffer byteBuffer;
1775 CIntVector orderIndices2;
1776 if (numFinded == 0 || IsExeExt(extension))
1777 {
1778 // signature search was here
1779 }
1780 else if (extension.IsEqualTo("000") || extension.IsEqualTo("001"))
1781 {
1782 int i = FindFormatForArchiveType(op.codecs, orderIndices, "rar");
1783 if (i >= 0)
1784 {
1785 const size_t kBufSize = (1 << 10);
1786 byteBuffer.Alloc(kBufSize);
1787 size_t processedSize = kBufSize;
1788 RINOK(ReadStream(op.stream, byteBuffer, &processedSize));
1789 if (processedSize >= 16)
1790 {
1791 const Byte *buf = byteBuffer;
1792 const Byte kRarHeader[] = { 0x52 , 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00 };
1793 if (TestSignature(buf, kRarHeader, 7) && buf[9] == 0x73 && (buf[10] & 1) != 0)
1794 {
1795 orderIndices2.Add(orderIndices[(unsigned)i]);
1796 orderIndices[(unsigned)i] = -1;
1797 if (i >= (int)numFinded)
1798 numFinded++;
1799 }
1800 }
1801 }
1802 }
1803 else
1804 {
1805 const size_t kBufSize = (1 << 10);
1806 byteBuffer.Alloc(kBufSize);
1807 size_t processedSize = kBufSize;
1808 RINOK(ReadStream(op.stream, byteBuffer, &processedSize));
1809 if (processedSize == 0)
1810 return S_FALSE;
1811
1812 /*
1813 check type order:
1814 1) matched extension, no signuature
1815 2) matched extension, matched signuature
1816 // 3) no signuature
1817 // 4) matched signuature
1818 */
1819
1820 MakeCheckOrder(op.codecs, orderIndices, numFinded, orderIndices2, NULL, 0);
1821 MakeCheckOrder(op.codecs, orderIndices, numFinded, orderIndices2, byteBuffer, processedSize);
1822 // MakeCheckOrder(op.codecs, orderIndices, orderIndices.Size(), orderIndices2, NULL, 0);
1823 // MakeCheckOrder(op.codecs, orderIndices, orderIndices.Size(), orderIndices2, byteBuffer, processedSize);
1824 }
1825
1826 FOR_VECTOR (i, orderIndices)
1827 {
1828 int val = orderIndices[i];
1829 if (val != -1)
1830 orderIndices2.Add(val);
1831 }
1832 orderIndices = orderIndices2;
1833 }
1834
1835 if (orderIndices.Size() >= 2)
1836 {
1837 int iIso = FindFormatForArchiveType(op.codecs, orderIndices, "iso");
1838 int iUdf = FindFormatForArchiveType(op.codecs, orderIndices, "udf");
1839 if (iUdf > iIso && iIso >= 0)
1840 {
1841 int isoIndex = orderIndices[(unsigned)iIso];
1842 int udfIndex = orderIndices[(unsigned)iUdf];
1843 orderIndices[(unsigned)iUdf] = isoIndex;
1844 orderIndices[(unsigned)iIso] = udfIndex;
1845 }
1846 }
1847
1848 numMainTypes = numFinded;
1849 isUnknownExt = (numMainTypes == 0) || isPrearcExt;
1850
1851 #else // _SFX
1852
1853 numMainTypes = orderIndices.Size();
1854
1855 // we need correct numMainTypes for mutlivolume SFX (if some volume is missing)
1856 if (numFinded != 0)
1857 numMainTypes = numFinded;
1858
1859 #endif
1860 }
1861
1862 UInt64 fileSize = 0;
1863 if (op.stream)
1864 {
1865 RINOK(op.stream->Seek(0, STREAM_SEEK_END, &fileSize));
1866 RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL));
1867 }
1868 FileSize = fileSize;
1869
1870
1871 #ifndef _SFX
1872
1873 CBoolArr skipFrontalFormat(op.codecs->Formats.Size());
1874 {
1875 FOR_VECTOR(i, op.codecs->Formats)
1876 skipFrontalFormat[i] = false;
1877 }
1878
1879 #endif
1880
1881 const COpenType &mode = op.openType;
1882
1883
1884
1885
1886
1887 if (mode.CanReturnArc)
1888 {
1889 // ---------- OPEN main type by extenssion ----------
1890
1891 unsigned numCheckTypes = orderIndices.Size();
1892 if (formatIndex >= 0)
1893 numCheckTypes = numMainTypes;
1894
1895 for (unsigned i = 0; i < numCheckTypes; i++)
1896 {
1897 FormatIndex = orderIndices[i];
1898
1899 // orderIndices[] item cannot be negative here
1900
1901 bool exactOnly = false;
1902
1903 #ifndef _SFX
1904
1905 const CArcInfoEx &ai = op.codecs->Formats[(unsigned)FormatIndex];
1906 // OutputDebugStringW(ai.Name);
1907 if (i >= numMainTypes)
1908 {
1909 if (!ai.Flags_BackwardOpen()
1910 // && !ai.Flags_PureStartOpen()
1911 )
1912 continue;
1913 exactOnly = true;
1914 }
1915
1916 #endif
1917
1918 // Some handlers do not set total bytes. So we set it here
1919 if (op.callback)
1920 RINOK(op.callback->SetTotal(NULL, &fileSize));
1921
1922 if (op.stream)
1923 {
1924 RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL));
1925 }
1926
1927 CMyComPtr<IInArchive> archive;
1928
1929 RINOK(PrepareToOpen(op, (unsigned)FormatIndex, archive));
1930 if (!archive)
1931 continue;
1932
1933 HRESULT result;
1934 if (op.stream)
1935 {
1936 UInt64 searchLimit = (!exactOnly && searchMarkerInHandler) ? maxStartOffset: 0;
1937 result = archive->Open(op.stream, &searchLimit, op.callback);
1938 }
1939 else
1940 {
1941 CMyComPtr<IArchiveOpenSeq> openSeq;
1942 archive.QueryInterface(IID_IArchiveOpenSeq, (void **)&openSeq);
1943 if (!openSeq)
1944 return E_NOTIMPL;
1945 result = openSeq->OpenSeq(op.seqStream);
1946 }
1947
1948 RINOK(ReadBasicProps(archive, 0, result));
1949
1950 if (result == S_FALSE)
1951 {
1952 bool isArc = ErrorInfo.IsArc_After_NonOpen();
1953
1954 #ifndef _SFX
1955 // if it's archive, we allow another open attempt for parser
1956 if (!mode.CanReturnParser || !isArc)
1957 skipFrontalFormat[(unsigned)FormatIndex] = true;
1958 #endif
1959
1960 if (exactOnly)
1961 continue;
1962
1963 if (i == 0 && numMainTypes == 1)
1964 {
1965 // we set NonOpenErrorInfo, only if there is only one main format (defined by extension).
1966 ErrorInfo.ErrorFormatIndex = FormatIndex;
1967 NonOpen_ErrorInfo = ErrorInfo;
1968
1969 if (!mode.CanReturnParser && isArc)
1970 {
1971 // if (formatIndex < 0 && !searchMarkerInHandler)
1972 {
1973 // if bad archive was detected, we don't need additional open attempts
1974 #ifndef _SFX
1975 if (!IsPreArcFormat(ai) /* || !mode.SkipSfxStub */)
1976 #endif
1977 return S_FALSE;
1978 }
1979 }
1980 }
1981
1982 /*
1983 #ifndef _SFX
1984 if (IsExeExt(extension) || ai.Flags_PreArc())
1985 {
1986 // openOnlyFullArc = false;
1987 // canReturnTailArc = true;
1988 // limitSignatureSearch = true;
1989 }
1990 #endif
1991 */
1992
1993 continue;
1994 }
1995
1996 RINOK(result);
1997
1998 #ifndef _SFX
1999
2000 bool isMainFormat = isMainFormatArr[(unsigned)FormatIndex];
2001 const COpenSpecFlags &specFlags = mode.GetSpec(isForced, isMainFormat, isUnknownExt);
2002
2003 bool thereIsTail = ErrorInfo.ThereIsTail;
2004 if (thereIsTail && mode.ZerosTailIsAllowed)
2005 {
2006 RINOK(CheckZerosTail(op, (UInt64)(Offset + (Int64)PhySize)));
2007 if (ErrorInfo.IgnoreTail)
2008 thereIsTail = false;
2009 }
2010
2011 if (Offset > 0)
2012 {
2013 if (exactOnly
2014 || !searchMarkerInHandler
2015 || !specFlags.CanReturn_NonStart()
2016 || (mode.MaxStartOffset_Defined && (UInt64)Offset > mode.MaxStartOffset))
2017 continue;
2018 }
2019 if (thereIsTail)
2020 {
2021 if (Offset > 0)
2022 {
2023 if (!specFlags.CanReturnMid)
2024 continue;
2025 }
2026 else if (!specFlags.CanReturnFrontal)
2027 continue;
2028 }
2029
2030 if (Offset > 0 || thereIsTail)
2031 {
2032 if (formatIndex < 0)
2033 {
2034 if (IsPreArcFormat(ai))
2035 {
2036 // openOnlyFullArc = false;
2037 // canReturnTailArc = true;
2038 /*
2039 if (mode.SkipSfxStub)
2040 limitSignatureSearch = true;
2041 */
2042 // if (mode.SkipSfxStub)
2043 {
2044 // skipFrontalFormat[FormatIndex] = true;
2045 continue;
2046 }
2047 }
2048 }
2049 }
2050
2051 #endif
2052
2053 Archive = archive;
2054 return S_OK;
2055 }
2056 }
2057
2058
2059
2060 #ifndef _SFX
2061
2062 if (!op.stream)
2063 return S_FALSE;
2064
2065 if (formatIndex >= 0 && !mode.CanReturnParser)
2066 {
2067 if (mode.MaxStartOffset_Defined)
2068 {
2069 if (mode.MaxStartOffset == 0)
2070 return S_FALSE;
2071 }
2072 else
2073 {
2074 const CArcInfoEx &ai = op.codecs->Formats[(unsigned)formatIndex];
2075 if (ai.FindExtension(extension) >= 0)
2076 {
2077 if (ai.Flags_FindSignature() && searchMarkerInHandler)
2078 return S_FALSE;
2079 }
2080 }
2081 }
2082
2083 NArchive::NParser::CHandler *handlerSpec = new NArchive::NParser::CHandler;
2084 CMyComPtr<IInArchive> handler = handlerSpec;
2085
2086 CExtractCallback_To_OpenCallback *extractCallback_To_OpenCallback_Spec = new CExtractCallback_To_OpenCallback;
2087 CMyComPtr<IArchiveExtractCallback> extractCallback_To_OpenCallback = extractCallback_To_OpenCallback_Spec;
2088 extractCallback_To_OpenCallback_Spec->Init(op.callback);
2089
2090 {
2091 // ---------- Check all possible START archives ----------
2092 // this code is better for full file archives than Parser's code.
2093
2094 CByteBuffer byteBuffer;
2095 bool endOfFile = false;
2096 size_t processedSize;
2097 {
2098 size_t bufSize = 1 << 20; // it must be larger than max signature offset or IsArcFunc offset ((1 << 19) + x for UDF)
2099 if (bufSize > fileSize)
2100 {
2101 bufSize = (size_t)fileSize;
2102 endOfFile = true;
2103 }
2104 byteBuffer.Alloc(bufSize);
2105 RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL));
2106 processedSize = bufSize;
2107 RINOK(ReadStream(op.stream, byteBuffer, &processedSize));
2108 if (processedSize == 0)
2109 return S_FALSE;
2110 if (processedSize < bufSize)
2111 endOfFile = true;
2112 }
2113 CUIntVector sortedFormats;
2114
2115 unsigned i;
2116
2117 int splitIndex = -1;
2118
2119 for (i = 0; i < orderIndices.Size(); i++)
2120 {
2121 // orderIndices[] item cannot be negative here
2122 unsigned form = (unsigned)orderIndices[i];
2123 if (skipFrontalFormat[form])
2124 continue;
2125
2126 const CArcInfoEx &ai = op.codecs->Formats[form];
2127
2128 if (ai.IsSplit())
2129 {
2130 splitIndex = (int)form;
2131 continue;
2132 }
2133
2134 if (ai.Flags_ByExtOnlyOpen())
2135 continue;
2136
2137 if (ai.IsArcFunc)
2138 {
2139 UInt32 isArcRes = ai.IsArcFunc(byteBuffer, processedSize);
2140 if (isArcRes == k_IsArc_Res_NO)
2141 continue;
2142 if (isArcRes == k_IsArc_Res_NEED_MORE && endOfFile)
2143 continue;
2144 // if (isArcRes == k_IsArc_Res_YES_LOW_PROB) continue;
2145 sortedFormats.Insert(0, form);
2146 continue;
2147 }
2148
2149 const bool isNewStyleSignature = IsNewStyleSignature(ai);
2150 bool needCheck = !isNewStyleSignature
2151 || ai.Signatures.IsEmpty()
2152 || ai.Flags_PureStartOpen()
2153 || ai.Flags_StartOpen()
2154 || ai.Flags_BackwardOpen();
2155
2156 if (isNewStyleSignature && !ai.Signatures.IsEmpty())
2157 {
2158 unsigned k;
2159 for (k = 0; k < ai.Signatures.Size(); k++)
2160 {
2161 const CByteBuffer &sig = ai.Signatures[k];
2162 if (processedSize < ai.SignatureOffset + sig.Size())
2163 {
2164 if (!endOfFile)
2165 needCheck = true;
2166 }
2167 else if (TestSignature(sig, byteBuffer + ai.SignatureOffset, sig.Size()))
2168 break;
2169 }
2170 if (k != ai.Signatures.Size())
2171 {
2172 sortedFormats.Insert(0, form);
2173 continue;
2174 }
2175 }
2176 if (needCheck)
2177 sortedFormats.Add(form);
2178 }
2179
2180 if (splitIndex >= 0)
2181 sortedFormats.Insert(0, (unsigned)splitIndex);
2182
2183 for (i = 0; i < sortedFormats.Size(); i++)
2184 {
2185 FormatIndex = (int)sortedFormats[i];
2186 const CArcInfoEx &ai = op.codecs->Formats[(unsigned)FormatIndex];
2187
2188 if (op.callback)
2189 RINOK(op.callback->SetTotal(NULL, &fileSize));
2190
2191 RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL));
2192
2193 CMyComPtr<IInArchive> archive;
2194 RINOK(PrepareToOpen(op, (unsigned)FormatIndex, archive));
2195 if (!archive)
2196 continue;
2197
2198 PRF(printf("\nSorted Open %S", (const wchar_t *)ai.Name));
2199 HRESULT result;
2200 {
2201 UInt64 searchLimit = 0;
2202 /*
2203 if (mode.CanReturnArc)
2204 result = archive->Open(op.stream, &searchLimit, op.callback);
2205 else
2206 */
2207 // if (!CanReturnArc), it's ParserMode, and we need phy size
2208 result = OpenArchiveSpec(archive,
2209 !mode.CanReturnArc, // needPhySize
2210 op.stream, &searchLimit, op.callback, extractCallback_To_OpenCallback);
2211 }
2212
2213 if (result == S_FALSE)
2214 {
2215 skipFrontalFormat[(unsigned)FormatIndex] = true;
2216 // FIXME: maybe we must use LenIsUnknown.
2217 // printf(" OpenForSize Error");
2218 continue;
2219 }
2220 RINOK(result);
2221
2222 RINOK(ReadBasicProps(archive, 0, result));
2223
2224 if (Offset > 0)
2225 {
2226 continue; // good handler doesn't return such Offset > 0
2227 // but there are some cases like false prefixed PK00 archive, when
2228 // we can support it?
2229 }
2230
2231 NArchive::NParser::CParseItem pi;
2232 pi.Offset = (UInt64)Offset;
2233 pi.Size = AvailPhySize;
2234
2235 // bool needScan = false;
2236
2237 if (!PhySizeDefined)
2238 {
2239 // it's for Z format
2240 pi.LenIsUnknown = true;
2241 // needScan = true;
2242 // phySize = arcRem;
2243 // nextNeedCheckStartOpen = false;
2244 }
2245
2246 /*
2247 if (OkPhySize_Defined)
2248 pi.OkSize = pi.OkPhySize;
2249 else
2250 pi.OkSize = pi.Size;
2251 */
2252
2253 pi.NormalizeOffset();
2254 // printf(" phySize = %8d", (unsigned)phySize);
2255
2256
2257 if (mode.CanReturnArc)
2258 {
2259 bool isMainFormat = isMainFormatArr[(unsigned)FormatIndex];
2260 const COpenSpecFlags &specFlags = mode.GetSpec(isForced, isMainFormat, isUnknownExt);
2261 bool openCur = false;
2262
2263 if (!ErrorInfo.ThereIsTail)
2264 openCur = true;
2265 else
2266 {
2267 if (mode.ZerosTailIsAllowed)
2268 {
2269 RINOK(CheckZerosTail(op, (UInt64)(Offset + (Int64)PhySize)));
2270 if (ErrorInfo.IgnoreTail)
2271 openCur = true;
2272 }
2273 if (!openCur)
2274 {
2275 openCur = specFlags.CanReturnFrontal;
2276 if (formatIndex < 0) // format is not forced
2277 {
2278 if (IsPreArcFormat(ai))
2279 {
2280 // if (mode.SkipSfxStub)
2281 {
2282 openCur = false;
2283 }
2284 }
2285 }
2286 }
2287 }
2288
2289 if (openCur)
2290 {
2291 InStream = op.stream;
2292 Archive = archive;
2293 return S_OK;
2294 }
2295 }
2296
2297 skipFrontalFormat[(unsigned)FormatIndex] = true;
2298
2299
2300 // if (!mode.CanReturnArc)
2301 /*
2302 if (!ErrorInfo.ThereIsTail)
2303 continue;
2304 */
2305 if (pi.Offset == 0 && !pi.LenIsUnknown && pi.Size >= FileSize)
2306 continue;
2307
2308 // printf("\nAdd offset = %d", (int)pi.Offset);
2309 RINOK(ReadParseItemProps(archive, ai, pi));
2310 handlerSpec->AddItem(pi);
2311 }
2312 }
2313
2314
2315
2316
2317
2318 // ---------- PARSER ----------
2319
2320 CUIntVector arc2sig; // formatIndex to signatureIndex
2321 CUIntVector sig2arc; // signatureIndex to formatIndex;
2322 {
2323 unsigned sum = 0;
2324 FOR_VECTOR (i, op.codecs->Formats)
2325 {
2326 arc2sig.Add(sum);
2327 const CObjectVector<CByteBuffer> &sigs = op.codecs->Formats[i].Signatures;
2328 sum += sigs.Size();
2329 FOR_VECTOR (k, sigs)
2330 sig2arc.Add(i);
2331 }
2332 }
2333
2334 {
2335 const size_t kBeforeSize = 1 << 16;
2336 const size_t kAfterSize = 1 << 20;
2337 const size_t kBufSize = 1 << 22; // it must be more than kBeforeSize + kAfterSize
2338
2339 const UInt32 kNumVals = (UInt32)1 << (kNumHashBytes * 8);
2340 CByteArr hashBuffer(kNumVals);
2341 Byte *hash = hashBuffer;
2342 memset(hash, 0xFF, kNumVals);
2343 Byte prevs[256];
2344 memset(prevs, 0xFF, sizeof(prevs));
2345 if (sig2arc.Size() >= 0xFF)
2346 return S_FALSE;
2347
2348 CUIntVector difficultFormats;
2349 CBoolArr difficultBools(256);
2350 {
2351 for (unsigned i = 0; i < 256; i++)
2352 difficultBools[i] = false;
2353 }
2354
2355 bool thereAreHandlersForSearch = false;
2356
2357 // UInt32 maxSignatureEnd = 0;
2358
2359 FOR_VECTOR (i, orderIndices)
2360 {
2361 int index = orderIndices[i];
2362 if (index < 0)
2363 continue;
2364 const CArcInfoEx &ai = op.codecs->Formats[(unsigned)index];
2365 if (ai.Flags_ByExtOnlyOpen())
2366 continue;
2367 bool isDifficult = false;
2368 // if (ai.Version < 0x91F) // we don't use parser with old DLL (before 9.31)
2369 if (!ai.NewInterface)
2370 isDifficult = true;
2371 else
2372 {
2373 if (ai.Flags_StartOpen())
2374 isDifficult = true;
2375 FOR_VECTOR (k, ai.Signatures)
2376 {
2377 const CByteBuffer &sig = ai.Signatures[k];
2378 /*
2379 UInt32 signatureEnd = ai.SignatureOffset + (UInt32)sig.Size();
2380 if (maxSignatureEnd < signatureEnd)
2381 maxSignatureEnd = signatureEnd;
2382 */
2383 if (sig.Size() < kNumHashBytes)
2384 {
2385 isDifficult = true;
2386 continue;
2387 }
2388 thereAreHandlersForSearch = true;
2389 UInt32 v = HASH_VAL(sig);
2390 unsigned sigIndex = arc2sig[(unsigned)index] + k;
2391 prevs[sigIndex] = hash[v];
2392 hash[v] = (Byte)sigIndex;
2393 }
2394 }
2395 if (isDifficult)
2396 {
2397 difficultFormats.Add((unsigned)index);
2398 difficultBools[(unsigned)index] = true;
2399 }
2400 }
2401
2402 if (!thereAreHandlersForSearch)
2403 {
2404 // openOnlyFullArc = true;
2405 // canReturnTailArc = true;
2406 }
2407
2408 RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL));
2409
2410 CLimitedCachedInStream *limitedStreamSpec = new CLimitedCachedInStream;
2411 CMyComPtr<IInStream> limitedStream = limitedStreamSpec;
2412 limitedStreamSpec->SetStream(op.stream);
2413
2414 CArchiveOpenCallback_Offset *openCallback_Offset_Spec = NULL;
2415 CMyComPtr<IArchiveOpenCallback> openCallback_Offset;
2416 if (op.callback)
2417 {
2418 openCallback_Offset_Spec = new CArchiveOpenCallback_Offset;
2419 openCallback_Offset = openCallback_Offset_Spec;
2420 openCallback_Offset_Spec->Callback = op.callback;
2421 openCallback_Offset_Spec->Callback.QueryInterface(IID_IArchiveOpenVolumeCallback, &openCallback_Offset_Spec->OpenVolumeCallback);
2422 #ifndef _NO_CRYPTO
2423 openCallback_Offset_Spec->Callback.QueryInterface(IID_ICryptoGetTextPassword, &openCallback_Offset_Spec->GetTextPassword);
2424 #endif
2425 }
2426
2427 if (op.callback)
2428 RINOK(op.callback->SetTotal(NULL, &fileSize));
2429
2430 CByteBuffer &byteBuffer = limitedStreamSpec->Buffer;
2431 byteBuffer.Alloc(kBufSize);
2432
2433 UInt64 callbackPrev = 0;
2434 bool needCheckStartOpen = true; // = true, if we need to test all archives types for current pos.
2435
2436 bool endOfFile = false;
2437 UInt64 bufPhyPos = 0;
2438 size_t bytesInBuf = 0;
2439 // UInt64 prevPos = 0;
2440
2441 // ---------- Main Scan Loop ----------
2442
2443 UInt64 pos = 0;
2444
2445 if (!mode.EachPos && handlerSpec->_items.Size() == 1)
2446 {
2447 NArchive::NParser::CParseItem &pi = handlerSpec->_items[0];
2448 if (!pi.LenIsUnknown && pi.Offset == 0)
2449 pos = pi.Size;
2450 }
2451
2452 for (;;)
2453 {
2454 // printf("\nPos = %d", (int)pos);
2455 UInt64 posInBuf = pos - bufPhyPos;
2456
2457 // if (pos > ((UInt64)1 << 35)) break;
2458
2459 if (!endOfFile)
2460 {
2461 if (bytesInBuf < kBufSize)
2462 {
2463 size_t processedSize = kBufSize - bytesInBuf;
2464 // printf("\nRead ask = %d", (unsigned)processedSize);
2465 UInt64 seekPos = bufPhyPos + bytesInBuf;
2466 RINOK(op.stream->Seek((Int64)(bufPhyPos + bytesInBuf), STREAM_SEEK_SET, NULL));
2467 RINOK(ReadStream(op.stream, byteBuffer + bytesInBuf, &processedSize));
2468 // printf(" processed = %d", (unsigned)processedSize);
2469 if (processedSize == 0)
2470 {
2471 fileSize = seekPos;
2472 endOfFile = true;
2473 }
2474 else
2475 {
2476 bytesInBuf += processedSize;
2477 limitedStreamSpec->SetCache(processedSize, (size_t)bufPhyPos);
2478 }
2479 continue;
2480 }
2481
2482 if (bytesInBuf < posInBuf)
2483 {
2484 UInt64 skipSize = posInBuf - bytesInBuf;
2485 if (skipSize <= kBeforeSize)
2486 {
2487 size_t keepSize = (size_t)(kBeforeSize - skipSize);
2488 // printf("\nmemmove skip = %d", (int)keepSize);
2489 memmove(byteBuffer, byteBuffer + bytesInBuf - keepSize, keepSize);
2490 bytesInBuf = keepSize;
2491 bufPhyPos = pos - keepSize;
2492 continue;
2493 }
2494 // printf("\nSkip %d", (int)(skipSize - kBeforeSize));
2495 // RINOK(op.stream->Seek(skipSize - kBeforeSize, STREAM_SEEK_CUR, NULL));
2496 bytesInBuf = 0;
2497 bufPhyPos = pos - kBeforeSize;
2498 continue;
2499 }
2500
2501 if (bytesInBuf - posInBuf < kAfterSize)
2502 {
2503 size_t beg = (size_t)posInBuf - kBeforeSize;
2504 // printf("\nmemmove for after beg = %d", (int)beg);
2505 memmove(byteBuffer, byteBuffer + beg, bytesInBuf - beg);
2506 bufPhyPos += beg;
2507 bytesInBuf -= beg;
2508 continue;
2509 }
2510 }
2511
2512 if (bytesInBuf <= (size_t)posInBuf)
2513 break;
2514
2515 bool useOffsetCallback = false;
2516 if (openCallback_Offset)
2517 {
2518 openCallback_Offset_Spec->Files = handlerSpec->_items.Size();
2519 openCallback_Offset_Spec->Offset = pos;
2520
2521 useOffsetCallback = (!op.openType.CanReturnArc || handlerSpec->_items.Size() > 1);
2522
2523 if (pos >= callbackPrev + (1 << 23))
2524 {
2525 RINOK(openCallback_Offset_Spec->SetCompleted(NULL, NULL));
2526 callbackPrev = pos;
2527 }
2528 }
2529
2530 {
2531 UInt64 endPos = bufPhyPos + bytesInBuf;
2532 if (fileSize < endPos)
2533 {
2534 FileSize = fileSize; // why ????
2535 fileSize = endPos;
2536 }
2537 }
2538
2539 const size_t availSize = bytesInBuf - (size_t)posInBuf;
2540 if (availSize < kNumHashBytes)
2541 break;
2542 size_t scanSize = availSize -
2543 ((availSize >= kAfterSize) ? kAfterSize : kNumHashBytes);
2544
2545 {
2546 /*
2547 UInt64 scanLimit = openOnlyFullArc ?
2548 maxSignatureEnd :
2549 op.openType.ScanSize + maxSignatureEnd;
2550 */
2551 if (!mode.CanReturnParser)
2552 {
2553 if (pos > maxStartOffset)
2554 break;
2555 UInt64 remScan = maxStartOffset - pos;
2556 if (scanSize > remScan)
2557 scanSize = (size_t)remScan;
2558 }
2559 }
2560
2561 scanSize++;
2562
2563 const Byte *buf = byteBuffer + (size_t)posInBuf;
2564 const Byte *bufLimit = buf + scanSize;
2565 size_t ppp = 0;
2566
2567 if (!needCheckStartOpen)
2568 {
2569 for (; buf < bufLimit && hash[HASH_VAL(buf)] == 0xFF; buf++);
2570 ppp = (size_t)(buf - (byteBuffer + (size_t)posInBuf));
2571 pos += ppp;
2572 if (buf == bufLimit)
2573 continue;
2574 }
2575
2576 UInt32 v = HASH_VAL(buf);
2577 bool nextNeedCheckStartOpen = true;
2578 unsigned i = hash[v];
2579 unsigned indexOfDifficult = 0;
2580
2581 // ---------- Open Loop for Current Pos ----------
2582 bool wasOpen = false;
2583
2584 for (;;)
2585 {
2586 unsigned index;
2587 bool isDifficult;
2588 if (needCheckStartOpen && indexOfDifficult < difficultFormats.Size())
2589 {
2590 index = difficultFormats[indexOfDifficult++];
2591 isDifficult = true;
2592 }
2593 else
2594 {
2595 if (i == 0xFF)
2596 break;
2597 index = sig2arc[i];
2598 unsigned sigIndex = i - arc2sig[index];
2599 i = prevs[i];
2600 if (needCheckStartOpen && difficultBools[index])
2601 continue;
2602 const CArcInfoEx &ai = op.codecs->Formats[index];
2603
2604 if (pos < ai.SignatureOffset)
2605 continue;
2606
2607 /*
2608 if (openOnlyFullArc)
2609 if (pos != ai.SignatureOffset)
2610 continue;
2611 */
2612
2613 const CByteBuffer &sig = ai.Signatures[sigIndex];
2614
2615 if (ppp + sig.Size() > availSize
2616 || !TestSignature(buf, sig, sig.Size()))
2617 continue;
2618 // printf("\nSignature OK: %10S %8x %5d", (const wchar_t *)ai.Name, (int)pos, (int)(pos - prevPos));
2619 // prevPos = pos;
2620 isDifficult = false;
2621 }
2622
2623 const CArcInfoEx &ai = op.codecs->Formats[index];
2624
2625
2626 if ((isDifficult && pos == 0) || ai.SignatureOffset == pos)
2627 {
2628 // we don't check same archive second time */
2629 if (skipFrontalFormat[index])
2630 continue;
2631 }
2632
2633 UInt64 startArcPos = pos;
2634 if (!isDifficult)
2635 {
2636 if (pos < ai.SignatureOffset)
2637 continue;
2638 startArcPos = pos - ai.SignatureOffset;
2639 /*
2640 // we don't need the check for Z files
2641 if (startArcPos < handlerSpec->GetLastEnd())
2642 continue;
2643 */
2644 }
2645
2646 if (ai.IsArcFunc && startArcPos >= bufPhyPos)
2647 {
2648 size_t offsetInBuf = (size_t)(startArcPos - bufPhyPos);
2649 if (offsetInBuf < bytesInBuf)
2650 {
2651 UInt32 isArcRes = ai.IsArcFunc(byteBuffer + offsetInBuf, bytesInBuf - offsetInBuf);
2652 if (isArcRes == k_IsArc_Res_NO)
2653 continue;
2654 if (isArcRes == k_IsArc_Res_NEED_MORE && endOfFile)
2655 continue;
2656 /*
2657 if (isArcRes == k_IsArc_Res_YES_LOW_PROB)
2658 {
2659 // if (pos != ai.SignatureOffset)
2660 continue;
2661 }
2662 */
2663 }
2664 // printf("\nIsArc OK: %S", (const wchar_t *)ai.Name);
2665 }
2666
2667 PRF(printf("\npos = %9I64d : %S", pos, (const wchar_t *)ai.Name));
2668
2669 const bool isMainFormat = isMainFormatArr[index];
2670 const COpenSpecFlags &specFlags = mode.GetSpec(isForced, isMainFormat, isUnknownExt);
2671
2672 CMyComPtr<IInArchive> archive;
2673 RINOK(PrepareToOpen(op, index, archive));
2674 if (!archive)
2675 return E_FAIL;
2676
2677 // OutputDebugStringW(ai.Name);
2678
2679 const UInt64 rem = fileSize - startArcPos;
2680
2681 UInt64 arcStreamOffset = 0;
2682
2683 if (ai.Flags_UseGlobalOffset())
2684 {
2685 limitedStreamSpec->InitAndSeek(0, fileSize);
2686 limitedStream->Seek((Int64)startArcPos, STREAM_SEEK_SET, NULL);
2687 }
2688 else
2689 {
2690 limitedStreamSpec->InitAndSeek(startArcPos, rem);
2691 arcStreamOffset = startArcPos;
2692 }
2693
2694 UInt64 maxCheckStartPosition = 0;
2695
2696 if (openCallback_Offset)
2697 {
2698 openCallback_Offset_Spec->Files = handlerSpec->_items.Size();
2699 openCallback_Offset_Spec->Offset = startArcPos;
2700 }
2701
2702 // HRESULT result = archive->Open(limitedStream, &maxCheckStartPosition, openCallback_Offset);
2703 extractCallback_To_OpenCallback_Spec->Files = 0;
2704 extractCallback_To_OpenCallback_Spec->Offset = startArcPos;
2705
2706 HRESULT result = OpenArchiveSpec(archive,
2707 true, // needPhySize
2708 limitedStream, &maxCheckStartPosition,
2709 useOffsetCallback ? (IArchiveOpenCallback *)openCallback_Offset : (IArchiveOpenCallback *)op.callback,
2710 extractCallback_To_OpenCallback);
2711
2712 RINOK(ReadBasicProps(archive, ai.Flags_UseGlobalOffset() ? 0 : startArcPos, result));
2713
2714 bool isOpen = false;
2715
2716 if (result == S_FALSE)
2717 {
2718 if (!mode.CanReturnParser)
2719 {
2720 if (formatIndex < 0 && ErrorInfo.IsArc_After_NonOpen())
2721 {
2722 ErrorInfo.ErrorFormatIndex = (int)index;
2723 NonOpen_ErrorInfo = ErrorInfo;
2724 // if archive was detected, we don't need additional open attempts
2725 return S_FALSE;
2726 }
2727 continue;
2728 }
2729 if (!ErrorInfo.IsArc_After_NonOpen() || !PhySizeDefined || PhySize == 0)
2730 continue;
2731 }
2732 else
2733 {
2734 if (PhySizeDefined && PhySize == 0)
2735 {
2736 PRF(printf(" phySizeDefined && PhySize == 0 "));
2737 // we skip that epmty archive case with unusual unexpected (PhySize == 0) from Code function.
2738 continue;
2739 }
2740 isOpen = true;
2741 RINOK(result);
2742 PRF(printf(" OK "));
2743 }
2744
2745 // fprintf(stderr, "\n %8X %S", startArcPos, Path);
2746 // printf("\nOpen OK: %S", ai.Name);
2747
2748
2749 NArchive::NParser::CParseItem pi;
2750 pi.Offset = startArcPos;
2751
2752 if (ai.Flags_UseGlobalOffset())
2753 pi.Offset = (UInt64)Offset;
2754 else if (Offset != 0)
2755 return E_FAIL;
2756
2757 UInt64 arcRem = FileSize - pi.Offset;
2758 UInt64 phySize = arcRem;
2759 bool phySizeDefined = PhySizeDefined;
2760 if (phySizeDefined)
2761 {
2762 if (pi.Offset + PhySize > FileSize)
2763 {
2764 // ErrorInfo.ThereIsTail = true;
2765 PhySize = FileSize - pi.Offset;
2766 }
2767 phySize = PhySize;
2768 }
2769 if (phySize == 0 || (UInt64)phySize > ((UInt64)1 << 63))
2770 return E_FAIL;
2771
2772 /*
2773 if (!ai.UseGlobalOffset)
2774 {
2775 if (phySize > arcRem)
2776 {
2777 ThereIsTail = true;
2778 phySize = arcRem;
2779 }
2780 }
2781 */
2782
2783 bool needScan = false;
2784
2785
2786 if (isOpen && !phySizeDefined)
2787 {
2788 // it's for Z format, or bzip2,gz,xz with phySize that was not detected
2789 pi.LenIsUnknown = true;
2790 needScan = true;
2791 phySize = arcRem;
2792 nextNeedCheckStartOpen = false;
2793 }
2794
2795 pi.Size = phySize;
2796 /*
2797 if (OkPhySize_Defined)
2798 pi.OkSize = OkPhySize;
2799 */
2800 pi.NormalizeOffset();
2801 // printf(" phySize = %8d", (unsigned)phySize);
2802
2803 /*
2804 if (needSkipFullArc)
2805 if (pi.Offset == 0 && phySizeDefined && pi.Size >= fileSize)
2806 continue;
2807 */
2808 if (pi.Offset == 0 && !pi.LenIsUnknown && pi.Size >= FileSize)
2809 {
2810 // it's possible for dmg archives
2811 if (!mode.CanReturnArc)
2812 continue;
2813 }
2814
2815 if (mode.EachPos)
2816 pos++;
2817 else if (needScan)
2818 {
2819 pos++;
2820 /*
2821 if (!OkPhySize_Defined)
2822 pos++;
2823 else
2824 pos = pi.Offset + pi.OkSize;
2825 */
2826 }
2827 else
2828 pos = pi.Offset + pi.Size;
2829
2830
2831 RINOK(ReadParseItemProps(archive, ai, pi));
2832
2833 if (pi.Offset < startArcPos && !mode.EachPos /* && phySizeDefined */)
2834 {
2835 /* It's for DMG format.
2836 This code deletes all previous items that are included to current item */
2837
2838 while (!handlerSpec->_items.IsEmpty())
2839 {
2840 {
2841 const NArchive::NParser::CParseItem &back = handlerSpec->_items.Back();
2842 if (back.Offset < pi.Offset)
2843 break;
2844 if (back.Offset + back.Size > pi.Offset + pi.Size)
2845 break;
2846 }
2847 handlerSpec->_items.DeleteBack();
2848 }
2849 }
2850
2851
2852 if (isOpen && mode.CanReturnArc && phySizeDefined)
2853 {
2854 // if (pi.Offset + pi.Size >= fileSize)
2855 bool openCur = false;
2856
2857 bool thereIsTail = ErrorInfo.ThereIsTail;
2858 if (thereIsTail && mode.ZerosTailIsAllowed)
2859 {
2860 RINOK(CheckZerosTail(op, (UInt64)((Int64)arcStreamOffset + Offset + (Int64)PhySize)));
2861 if (ErrorInfo.IgnoreTail)
2862 thereIsTail = false;
2863 }
2864
2865 if (pi.Offset != 0)
2866 {
2867 if (!pi.IsNotArcType)
2868 {
2869 if (thereIsTail)
2870 openCur = specFlags.CanReturnMid;
2871 else
2872 openCur = specFlags.CanReturnTail;
2873 }
2874 }
2875 else
2876 {
2877 if (!thereIsTail)
2878 openCur = true;
2879 else
2880 openCur = specFlags.CanReturnFrontal;
2881
2882 if (formatIndex >= -2)
2883 openCur = true;
2884 }
2885
2886 if (formatIndex < 0 && pi.IsSelfExe /* && mode.SkipSfxStub */)
2887 openCur = false;
2888
2889 // We open file as SFX, if there is front archive or first archive is "Self Executable"
2890 if (!openCur && !pi.IsSelfExe && !thereIsTail &&
2891 (!pi.IsNotArcType || pi.Offset == 0))
2892 {
2893 if (handlerSpec->_items.IsEmpty())
2894 {
2895 if (specFlags.CanReturnTail)
2896 openCur = true;
2897 }
2898 else if (handlerSpec->_items.Size() == 1)
2899 {
2900 if (handlerSpec->_items[0].IsSelfExe)
2901 {
2902 if (mode.SpecUnknownExt.CanReturnTail)
2903 openCur = true;
2904 }
2905 }
2906 }
2907
2908 if (openCur)
2909 {
2910 InStream = op.stream;
2911 Archive = archive;
2912 FormatIndex = (int)index;
2913 ArcStreamOffset = arcStreamOffset;
2914 return S_OK;
2915 }
2916 }
2917
2918 /*
2919 if (openOnlyFullArc)
2920 {
2921 ErrorInfo.ClearErrors();
2922 return S_FALSE;
2923 }
2924 */
2925
2926 pi.FormatIndex = (int)index;
2927
2928 // printf("\nAdd offset = %d", (int)pi.Offset);
2929 handlerSpec->AddItem(pi);
2930 wasOpen = true;
2931 break;
2932 }
2933 // ---------- End of Open Loop for Current Pos ----------
2934
2935 if (!wasOpen)
2936 pos++;
2937 needCheckStartOpen = (nextNeedCheckStartOpen && wasOpen);
2938 }
2939 // ---------- End of Main Scan Loop ----------
2940
2941 /*
2942 if (handlerSpec->_items.Size() == 1)
2943 {
2944 const NArchive::NParser::CParseItem &pi = handlerSpec->_items[0];
2945 if (pi.Size == fileSize && pi.Offset == 0)
2946 {
2947 Archive = archive;
2948 FormatIndex2 = pi.FormatIndex;
2949 return S_OK;
2950 }
2951 }
2952 */
2953
2954 if (mode.CanReturnParser)
2955 {
2956 bool returnParser = (handlerSpec->_items.Size() == 1); // it's possible if fileSize was not correct at start of parsing
2957 handlerSpec->AddUnknownItem(fileSize);
2958 if (handlerSpec->_items.Size() == 0)
2959 return S_FALSE;
2960 if (returnParser || handlerSpec->_items.Size() != 1)
2961 {
2962 // return S_FALSE;
2963 handlerSpec->_stream = op.stream;
2964 Archive = handler;
2965 ErrorInfo.ClearErrors();
2966 IsParseArc = true;
2967 FormatIndex = -1; // It's parser
2968 Offset = 0;
2969 return S_OK;
2970 }
2971 }
2972 }
2973
2974 #endif
2975
2976 if (!Archive)
2977 return S_FALSE;
2978 return S_OK;
2979}
2980
2981
2982
2983
2984HRESULT CArc::OpenStream(const COpenOptions &op)
2985{
2986 RINOK(OpenStream2(op));
2987 // PrintNumber("op.formatIndex 3", op.formatIndex);
2988
2989 if (Archive)
2990 {
2991 GetRawProps.Release();
2992 GetRootProps.Release();
2993 Archive->QueryInterface(IID_IArchiveGetRawProps, (void **)&GetRawProps);
2994 Archive->QueryInterface(IID_IArchiveGetRootProps, (void **)&GetRootProps);
2995
2996 RINOK(Archive_GetArcBoolProp(Archive, kpidIsTree, IsTree));
2997 RINOK(Archive_GetArcBoolProp(Archive, kpidIsDeleted, Ask_Deleted));
2998 RINOK(Archive_GetArcBoolProp(Archive, kpidIsAltStream, Ask_AltStream));
2999 RINOK(Archive_GetArcBoolProp(Archive, kpidIsAux, Ask_Aux));
3000 RINOK(Archive_GetArcBoolProp(Archive, kpidINode, Ask_INode));
3001 RINOK(Archive_GetArcBoolProp(Archive, kpidReadOnly, IsReadOnly));
3002
3003 const UString fileName = ExtractFileNameFromPath(Path);
3004 UString extension;
3005 {
3006 int dotPos = fileName.ReverseFind_Dot();
3007 if (dotPos >= 0)
3008 extension = fileName.Ptr((unsigned)(dotPos + 1));
3009 }
3010
3011 DefaultName.Empty();
3012 if (FormatIndex >= 0)
3013 {
3014 const CArcInfoEx &ai = op.codecs->Formats[(unsigned)FormatIndex];
3015 if (ai.Exts.Size() == 0)
3016 DefaultName = GetDefaultName2(fileName, UString(), UString());
3017 else
3018 {
3019 int subExtIndex = ai.FindExtension(extension);
3020 if (subExtIndex < 0)
3021 subExtIndex = 0;
3022 const CArcExtInfo &extInfo = ai.Exts[(unsigned)subExtIndex];
3023 DefaultName = GetDefaultName2(fileName, extInfo.Ext, extInfo.AddExt);
3024 }
3025 }
3026 }
3027
3028 return S_OK;
3029}
3030
3031#ifdef _SFX
3032
3033#ifdef _WIN32
3034 #define k_ExeExt ".exe"
3035 static const unsigned k_ExeExt_Len = 4;
3036#else
3037 #define k_ExeExt ""
3038 static const unsigned k_ExeExt_Len = 0;
3039#endif
3040
3041#endif
3042
3043HRESULT CArc::OpenStreamOrFile(COpenOptions &op)
3044{
3045 CMyComPtr<IInStream> fileStream;
3046 CMyComPtr<ISequentialInStream> seqStream;
3047 CInFileStream *fileStreamSpec = NULL;
3048
3049 if (op.stdInMode)
3050 {
3051 seqStream = new CStdInFileStream;
3052 op.seqStream = seqStream;
3053 }
3054 else if (!op.stream)
3055 {
3056 fileStreamSpec = new CInFileStream;
3057 fileStream = fileStreamSpec;
3058 Path = filePath;
3059 if (!fileStreamSpec->Open(us2fs(Path)))
3060 return GetLastError_noZero_HRESULT();
3061 op.stream = fileStream;
3062 #ifdef _SFX
3063 IgnoreSplit = true;
3064 #endif
3065 }
3066
3067 /*
3068 if (callback)
3069 {
3070 UInt64 fileSize;
3071 RINOK(op.stream->Seek(0, STREAM_SEEK_END, &fileSize));
3072 RINOK(op.callback->SetTotal(NULL, &fileSize))
3073 }
3074 */
3075
3076 HRESULT res = OpenStream(op);
3077 IgnoreSplit = false;
3078
3079 #ifdef _SFX
3080
3081 if (res != S_FALSE
3082 || !fileStreamSpec
3083 || !op.callbackSpec
3084 || NonOpen_ErrorInfo.IsArc_After_NonOpen())
3085 return res;
3086
3087 {
3088 if (filePath.Len() > k_ExeExt_Len
3089 && StringsAreEqualNoCase_Ascii(filePath.RightPtr(k_ExeExt_Len), k_ExeExt))
3090 {
3091 const UString path2 = filePath.Left(filePath.Len() - k_ExeExt_Len);
3092 FOR_VECTOR (i, op.codecs->Formats)
3093 {
3094 const CArcInfoEx &ai = op.codecs->Formats[i];
3095 if (ai.IsSplit())
3096 continue;
3097 UString path3 = path2;
3098 path3 += '.';
3099 path3 += ai.GetMainExt(); // "7z" for SFX.
3100 Path = path3;
3101 Path += ".001";
3102 bool isOk = op.callbackSpec->SetSecondFileInfo(us2fs(Path));
3103 if (!isOk)
3104 {
3105 Path = path3;
3106 isOk = op.callbackSpec->SetSecondFileInfo(us2fs(Path));
3107 }
3108 if (isOk)
3109 {
3110 if (fileStreamSpec->Open(us2fs(Path)))
3111 {
3112 op.stream = fileStream;
3113 NonOpen_ErrorInfo.ClearErrors_Full();
3114 if (OpenStream(op) == S_OK)
3115 return S_OK;
3116 }
3117 }
3118 }
3119 }
3120 }
3121
3122 #endif
3123
3124 return res;
3125}
3126
3127void CArchiveLink::KeepModeForNextOpen()
3128{
3129 for (unsigned i = Arcs.Size(); i != 0;)
3130 {
3131 i--;
3132 CMyComPtr<IArchiveKeepModeForNextOpen> keep;
3133 Arcs[i].Archive->QueryInterface(IID_IArchiveKeepModeForNextOpen, (void **)&keep);
3134 if (keep)
3135 keep->KeepModeForNextOpen();
3136 }
3137}
3138
3139HRESULT CArchiveLink::Close()
3140{
3141 for (unsigned i = Arcs.Size(); i != 0;)
3142 {
3143 i--;
3144 RINOK(Arcs[i].Close());
3145 }
3146 IsOpen = false;
3147 // ErrorsText.Empty();
3148 return S_OK;
3149}
3150
3151void CArchiveLink::Release()
3152{
3153 // NonOpenErrorFormatIndex = -1;
3154 NonOpen_ErrorInfo.ClearErrors();
3155 NonOpen_ArcPath.Empty();
3156 while (!Arcs.IsEmpty())
3157 Arcs.DeleteBack();
3158}
3159
3160/*
3161void CArchiveLink::Set_ErrorsText()
3162{
3163 FOR_VECTOR(i, Arcs)
3164 {
3165 const CArc &arc = Arcs[i];
3166 if (!arc.ErrorFlagsText.IsEmpty())
3167 {
3168 if (!ErrorsText.IsEmpty())
3169 ErrorsText.Add_LF();
3170 ErrorsText += GetUnicodeString(arc.ErrorFlagsText);
3171 }
3172 if (!arc.ErrorMessage.IsEmpty())
3173 {
3174 if (!ErrorsText.IsEmpty())
3175 ErrorsText.Add_LF();
3176 ErrorsText += arc.ErrorMessage;
3177 }
3178
3179 if (!arc.WarningMessage.IsEmpty())
3180 {
3181 if (!ErrorsText.IsEmpty())
3182 ErrorsText.Add_LF();
3183 ErrorsText += arc.WarningMessage;
3184 }
3185 }
3186}
3187*/
3188
3189HRESULT CArchiveLink::Open(COpenOptions &op)
3190{
3191 Release();
3192 if (op.types->Size() >= 32)
3193 return E_NOTIMPL;
3194
3195 HRESULT resSpec;
3196
3197 for (;;)
3198 {
3199 resSpec = S_OK;
3200
3201 op.openType = COpenType();
3202 if (op.types->Size() >= 1)
3203 {
3204 COpenType latest;
3205 if (Arcs.Size() < op.types->Size())
3206 latest = (*op.types)[op.types->Size() - Arcs.Size() - 1];
3207 else
3208 {
3209 latest = (*op.types)[0];
3210 if (!latest.Recursive)
3211 break;
3212 }
3213 op.openType = latest;
3214 }
3215 else if (Arcs.Size() >= 32)
3216 break;
3217
3218 /*
3219 op.formatIndex = -1;
3220 if (op.types->Size() >= 1)
3221 {
3222 int latest;
3223 if (Arcs.Size() < op.types->Size())
3224 latest = (*op.types)[op.types->Size() - Arcs.Size() - 1];
3225 else
3226 {
3227 latest = (*op.types)[0];
3228 if (latest != -2 && latest != -3)
3229 break;
3230 }
3231 if (latest >= 0)
3232 op.formatIndex = latest;
3233 else if (latest == -1 || latest == -2)
3234 {
3235 // default
3236 }
3237 else if (latest == -3)
3238 op.formatIndex = -2;
3239 else
3240 op.formatIndex = latest + 2;
3241 }
3242 else if (Arcs.Size() >= 32)
3243 break;
3244 */
3245
3246 if (Arcs.IsEmpty())
3247 {
3248 CArc arc;
3249 arc.filePath = op.filePath;
3250 arc.Path = op.filePath;
3251 arc.SubfileIndex = (UInt32)(Int32)-1;
3252 HRESULT result = arc.OpenStreamOrFile(op);
3253 if (result != S_OK)
3254 {
3255 if (result == S_FALSE)
3256 {
3257 NonOpen_ErrorInfo = arc.NonOpen_ErrorInfo;
3258 // NonOpenErrorFormatIndex = arc.ErrorFormatIndex;
3259 NonOpen_ArcPath = arc.Path;
3260 }
3261 return result;
3262 }
3263 Arcs.Add(arc);
3264 continue;
3265 }
3266
3267 // PrintNumber("op.formatIndex 11", op.formatIndex);
3268
3269 const CArc &arc = Arcs.Back();
3270
3271 if (op.types->Size() > Arcs.Size())
3272 resSpec = E_NOTIMPL;
3273
3274 UInt32 mainSubfile;
3275 {
3276 NCOM::CPropVariant prop;
3277 RINOK(arc.Archive->GetArchiveProperty(kpidMainSubfile, &prop));
3278 if (prop.vt == VT_UI4)
3279 mainSubfile = prop.ulVal;
3280 else
3281 break;
3282 UInt32 numItems;
3283 RINOK(arc.Archive->GetNumberOfItems(&numItems));
3284 if (mainSubfile >= numItems)
3285 break;
3286 }
3287
3288
3289 CMyComPtr<IInArchiveGetStream> getStream;
3290 if (arc.Archive->QueryInterface(IID_IInArchiveGetStream, (void **)&getStream) != S_OK || !getStream)
3291 break;
3292
3293 CMyComPtr<ISequentialInStream> subSeqStream;
3294 if (getStream->GetStream(mainSubfile, &subSeqStream) != S_OK || !subSeqStream)
3295 break;
3296
3297 CMyComPtr<IInStream> subStream;
3298 if (subSeqStream.QueryInterface(IID_IInStream, &subStream) != S_OK || !subStream)
3299 break;
3300
3301 CArc arc2;
3302 RINOK(arc.GetItemPath(mainSubfile, arc2.Path));
3303
3304 bool zerosTailIsAllowed;
3305 RINOK(Archive_GetItemBoolProp(arc.Archive, mainSubfile, kpidZerosTailIsAllowed, zerosTailIsAllowed));
3306
3307
3308 if (op.callback)
3309 {
3310 CMyComPtr<IArchiveOpenSetSubArchiveName> setSubArchiveName;
3311 op.callback->QueryInterface(IID_IArchiveOpenSetSubArchiveName, (void **)&setSubArchiveName);
3312 if (setSubArchiveName)
3313 setSubArchiveName->SetSubArchiveName(arc2.Path);
3314 }
3315
3316 arc2.SubfileIndex = mainSubfile;
3317
3318 // CIntVector incl;
3319 CIntVector excl;
3320
3321 COpenOptions op2;
3322 #ifndef _SFX
3323 op2.props = op.props;
3324 #endif
3325 op2.codecs = op.codecs;
3326 // op2.types = &incl;
3327 op2.openType = op.openType;
3328 op2.openType.ZerosTailIsAllowed = zerosTailIsAllowed;
3329 op2.excludedFormats = &excl;
3330 op2.stdInMode = false;
3331 op2.stream = subStream;
3332 op2.filePath = arc2.Path;
3333 op2.callback = op.callback;
3334 op2.callbackSpec = op.callbackSpec;
3335
3336
3337 HRESULT result = arc2.OpenStream(op2);
3338 resSpec = (op.types->Size() == 0 ? S_OK : S_FALSE);
3339 if (result == S_FALSE)
3340 {
3341 NonOpen_ErrorInfo = arc2.ErrorInfo;
3342 NonOpen_ArcPath = arc2.Path;
3343 break;
3344 }
3345 RINOK(result);
3346 RINOK(arc.GetItemMTime(mainSubfile, arc2.MTime, arc2.MTimeDefined));
3347 Arcs.Add(arc2);
3348 }
3349 IsOpen = !Arcs.IsEmpty();
3350 return resSpec;
3351}
3352
3353HRESULT CArchiveLink::Open2(COpenOptions &op, IOpenCallbackUI *callbackUI)
3354{
3355 VolumesSize = 0;
3356 COpenCallbackImp *openCallbackSpec = new COpenCallbackImp;
3357 CMyComPtr<IArchiveOpenCallback> callback = openCallbackSpec;
3358 openCallbackSpec->Callback = callbackUI;
3359
3360 FString prefix, name;
3361
3362 if (!op.stream && !op.stdInMode)
3363 {
3364 NFile::NDir::GetFullPathAndSplit(us2fs(op.filePath), prefix, name);
3365 RINOK(openCallbackSpec->Init2(prefix, name));
3366 }
3367 else
3368 {
3369 openCallbackSpec->SetSubArchiveName(op.filePath);
3370 }
3371
3372 op.callback = callback;
3373 op.callbackSpec = openCallbackSpec;
3374
3375 HRESULT res = Open(op);
3376
3377 PasswordWasAsked = openCallbackSpec->PasswordWasAsked;
3378 // Password = openCallbackSpec->Password;
3379
3380 RINOK(res);
3381 // VolumePaths.Add(fs2us(prefix + name));
3382
3383 FOR_VECTOR (i, openCallbackSpec->FileNames_WasUsed)
3384 {
3385 if (openCallbackSpec->FileNames_WasUsed[i])
3386 {
3387 VolumePaths.Add(fs2us(prefix) + openCallbackSpec->FileNames[i]);
3388 VolumesSize += openCallbackSpec->FileSizes[i];
3389 }
3390 }
3391 // VolumesSize = openCallbackSpec->TotalSize;
3392 return S_OK;
3393}
3394
3395HRESULT CArc::ReOpen(const COpenOptions &op, IArchiveOpenCallback *openCallback_Additional)
3396{
3397 ErrorInfo.ClearErrors();
3398 ErrorInfo.ErrorFormatIndex = -1;
3399
3400 UInt64 fileSize = 0;
3401 if (op.stream)
3402 {
3403 RINOK(op.stream->Seek(0, STREAM_SEEK_END, &fileSize));
3404 RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL));
3405 }
3406 FileSize = fileSize;
3407
3408 CMyComPtr<IInStream> stream2;
3409 Int64 globalOffset = GetGlobalOffset();
3410 if (globalOffset <= 0)
3411 stream2 = op.stream;
3412 else
3413 {
3414 CTailInStream *tailStreamSpec = new CTailInStream;
3415 stream2 = tailStreamSpec;
3416 tailStreamSpec->Stream = op.stream;
3417 tailStreamSpec->Offset = (UInt64)globalOffset;
3418 tailStreamSpec->Init();
3419 RINOK(tailStreamSpec->SeekToStart());
3420 }
3421
3422 // There are archives with embedded STUBs (like ZIP), so we must support signature scanning
3423 // But for another archives we can use 0 here. So the code can be fixed !!!
3424 UInt64 maxStartPosition = kMaxCheckStartPosition;
3425 IArchiveOpenCallback *openCallback = openCallback_Additional;
3426 if (!openCallback)
3427 openCallback = op.callback;
3428 HRESULT res = Archive->Open(stream2, &maxStartPosition, openCallback);
3429
3430 if (res == S_OK)
3431 {
3432 RINOK(ReadBasicProps(Archive, (UInt64)globalOffset, res));
3433 ArcStreamOffset = (UInt64)globalOffset;
3434 if (ArcStreamOffset != 0)
3435 InStream = op.stream;
3436 }
3437 return res;
3438}
3439
3440HRESULT CArchiveLink::Open3(COpenOptions &op, IOpenCallbackUI *callbackUI)
3441{
3442 HRESULT res = Open2(op, callbackUI);
3443 if (callbackUI)
3444 {
3445 RINOK(callbackUI->Open_Finished());
3446 }
3447 return res;
3448}
3449
3450HRESULT CArchiveLink::ReOpen(COpenOptions &op)
3451{
3452 if (Arcs.Size() > 1)
3453 return E_NOTIMPL;
3454
3455 CObjectVector<COpenType> inc;
3456 CIntVector excl;
3457
3458 op.types = &inc;
3459 op.excludedFormats = &excl;
3460 op.stdInMode = false;
3461 op.stream = NULL;
3462 if (Arcs.Size() == 0) // ???
3463 return Open2(op, NULL);
3464
3465 COpenCallbackImp *openCallbackSpec = new COpenCallbackImp;
3466 CMyComPtr<IArchiveOpenCallback> openCallbackNew = openCallbackSpec;
3467
3468 openCallbackSpec->Callback = NULL;
3469 openCallbackSpec->ReOpenCallback = op.callback;
3470 {
3471 FString dirPrefix, fileName;
3472 NFile::NDir::GetFullPathAndSplit(us2fs(op.filePath), dirPrefix, fileName);
3473 RINOK(openCallbackSpec->Init2(dirPrefix, fileName));
3474 }
3475
3476
3477 CInFileStream *fileStreamSpec = new CInFileStream;
3478 CMyComPtr<IInStream> stream(fileStreamSpec);
3479 if (!fileStreamSpec->Open(us2fs(op.filePath)))
3480 return GetLastError_noZero_HRESULT();
3481 op.stream = stream;
3482
3483 CArc &arc = Arcs[0];
3484 HRESULT res = arc.ReOpen(op, openCallbackNew);
3485
3486 PasswordWasAsked = openCallbackSpec->PasswordWasAsked;
3487 // Password = openCallbackSpec->Password;
3488
3489 IsOpen = (res == S_OK);
3490 return res;
3491}
3492
3493#ifndef _SFX
3494
3495bool ParseComplexSize(const wchar_t *s, UInt64 &result);
3496bool ParseComplexSize(const wchar_t *s, UInt64 &result)
3497{
3498 result = 0;
3499 const wchar_t *end;
3500 UInt64 number = ConvertStringToUInt64(s, &end);
3501 if (end == s)
3502 return false;
3503 if (*end == 0)
3504 {
3505 result = number;
3506 return true;
3507 }
3508 if (end[1] != 0)
3509 return false;
3510 unsigned numBits;
3511 switch (MyCharLower_Ascii(*end))
3512 {
3513 case 'b': result = number; return true;
3514 case 'k': numBits = 10; break;
3515 case 'm': numBits = 20; break;
3516 case 'g': numBits = 30; break;
3517 case 't': numBits = 40; break;
3518 default: return false;
3519 }
3520 if (number >= ((UInt64)1 << (64 - numBits)))
3521 return false;
3522 result = number << numBits;
3523 return true;
3524}
3525
3526static bool ParseTypeParams(const UString &s, COpenType &type)
3527{
3528 if (s[0] == 0)
3529 return true;
3530 if (s[1] == 0)
3531 {
3532 switch ((unsigned)(Byte)s[0])
3533 {
3534 case 'e': type.EachPos = true; return true;
3535 case 'a': type.CanReturnArc = true; return true;
3536 case 'r': type.Recursive = true; return true;
3537 }
3538 return false;
3539 }
3540 if (s[0] == 's')
3541 {
3542 UInt64 result;
3543 if (!ParseComplexSize(s.Ptr(1), result))
3544 return false;
3545 type.MaxStartOffset = result;
3546 type.MaxStartOffset_Defined = true;
3547 return true;
3548 }
3549
3550 return false;
3551}
3552
3553static bool ParseType(CCodecs &codecs, const UString &s, COpenType &type)
3554{
3555 int pos2 = s.Find(L':');
3556
3557 {
3558 UString name;
3559 if (pos2 < 0)
3560 {
3561 name = s;
3562 pos2 = (int)s.Len();
3563 }
3564 else
3565 {
3566 name = s.Left((unsigned)pos2);
3567 pos2++;
3568 }
3569
3570 int index = codecs.FindFormatForArchiveType(name);
3571 type.Recursive = false;
3572
3573 if (index < 0)
3574 {
3575 if (name[0] == '*')
3576 {
3577 if (name[1] != 0)
3578 return false;
3579 }
3580 else if (name[0] == '#')
3581 {
3582 if (name[1] != 0)
3583 return false;
3584 type.CanReturnArc = false;
3585 type.CanReturnParser = true;
3586 }
3587 else if (name.IsEqualTo_Ascii_NoCase("hash"))
3588 {
3589 // type.CanReturnArc = false;
3590 // type.CanReturnParser = false;
3591 type.IsHashType = true;
3592 }
3593 else
3594 return false;
3595 }
3596
3597 type.FormatIndex = index;
3598
3599 }
3600
3601 for (unsigned i = (unsigned)pos2; i < s.Len();)
3602 {
3603 int next = s.Find(L':', i);
3604 if (next < 0)
3605 next = (int)s.Len();
3606 const UString name = s.Mid(i, (unsigned)next - i);
3607 if (name.IsEmpty())
3608 return false;
3609 if (!ParseTypeParams(name, type))
3610 return false;
3611 i = (unsigned)next + 1;
3612 }
3613
3614 return true;
3615}
3616
3617bool ParseOpenTypes(CCodecs &codecs, const UString &s, CObjectVector<COpenType> &types)
3618{
3619 types.Clear();
3620 bool isHashType = false;
3621 for (unsigned pos = 0; pos < s.Len();)
3622 {
3623 int pos2 = s.Find(L'.', pos);
3624 if (pos2 < 0)
3625 pos2 = (int)s.Len();
3626 UString name = s.Mid(pos, (unsigned)pos2 - pos);
3627 if (name.IsEmpty())
3628 return false;
3629 COpenType type;
3630 if (!ParseType(codecs, name, type))
3631 return false;
3632 if (isHashType)
3633 return false;
3634 if (type.IsHashType)
3635 isHashType = true;
3636 types.Add(type);
3637 pos = (unsigned)pos2 + 1;
3638 }
3639 return true;
3640}
3641
3642/*
3643bool IsHashType(const CObjectVector<COpenType> &types)
3644{
3645 if (types.Size() != 1)
3646 return false;
3647 return types[0].IsHashType;
3648}
3649*/
3650
3651
3652#endif
diff --git a/CPP/7zip/UI/Common/OpenArchive.h b/CPP/7zip/UI/Common/OpenArchive.h
new file mode 100644
index 0000000..4e1192c
--- /dev/null
+++ b/CPP/7zip/UI/Common/OpenArchive.h
@@ -0,0 +1,454 @@
1// OpenArchive.h
2
3#ifndef __OPEN_ARCHIVE_H
4#define __OPEN_ARCHIVE_H
5
6#include "../../../Windows/PropVariant.h"
7
8#include "ArchiveOpenCallback.h"
9#include "LoadCodecs.h"
10#include "Property.h"
11
12#ifndef _SFX
13
14#define SUPPORT_ALT_STREAMS
15
16#endif
17
18HRESULT Archive_GetItemBoolProp(IInArchive *arc, UInt32 index, PROPID propID, bool &result) throw();
19HRESULT Archive_IsItem_Dir(IInArchive *arc, UInt32 index, bool &result) throw();
20HRESULT Archive_IsItem_Aux(IInArchive *arc, UInt32 index, bool &result) throw();
21HRESULT Archive_IsItem_AltStream(IInArchive *arc, UInt32 index, bool &result) throw();
22HRESULT Archive_IsItem_Deleted(IInArchive *arc, UInt32 index, bool &deleted) throw();
23
24#ifdef SUPPORT_ALT_STREAMS
25int FindAltStreamColon_in_Path(const wchar_t *path);
26#endif
27
28/*
29struct COptionalOpenProperties
30{
31 UString FormatName;
32 CObjectVector<CProperty> Props;
33};
34*/
35
36#ifdef _SFX
37#define OPEN_PROPS_DECL
38#else
39#define OPEN_PROPS_DECL const CObjectVector<CProperty> *props;
40// #define OPEN_PROPS_DECL , const CObjectVector<COptionalOpenProperties> *props
41#endif
42
43struct COpenSpecFlags
44{
45 // bool CanReturnFull;
46 bool CanReturnFrontal;
47 bool CanReturnTail;
48 bool CanReturnMid;
49
50 bool CanReturn_NonStart() const { return CanReturnTail || CanReturnMid; }
51
52 COpenSpecFlags():
53 // CanReturnFull(true),
54 CanReturnFrontal(false),
55 CanReturnTail(false),
56 CanReturnMid(false)
57 {}
58};
59
60struct COpenType
61{
62 int FormatIndex;
63
64 COpenSpecFlags SpecForcedType;
65 COpenSpecFlags SpecMainType;
66 COpenSpecFlags SpecWrongExt;
67 COpenSpecFlags SpecUnknownExt;
68
69 bool Recursive;
70
71 bool CanReturnArc;
72 bool CanReturnParser;
73 bool IsHashType;
74 bool EachPos;
75
76 // bool SkipSfxStub;
77 // bool ExeAsUnknown;
78
79 bool ZerosTailIsAllowed;
80
81 bool MaxStartOffset_Defined;
82 UInt64 MaxStartOffset;
83
84 const COpenSpecFlags &GetSpec(bool isForced, bool isMain, bool isUnknown) const
85 {
86 return isForced ? SpecForcedType : (isMain ? SpecMainType : (isUnknown ? SpecUnknownExt : SpecWrongExt));
87 }
88
89 COpenType():
90 FormatIndex(-1),
91 Recursive(true),
92 CanReturnArc(true),
93 CanReturnParser(false),
94 IsHashType(false),
95 EachPos(false),
96 // SkipSfxStub(true),
97 // ExeAsUnknown(true),
98 ZerosTailIsAllowed(false),
99 MaxStartOffset_Defined(false),
100 MaxStartOffset(0)
101 {
102 SpecForcedType.CanReturnFrontal = true;
103 SpecForcedType.CanReturnTail = true;
104 SpecForcedType.CanReturnMid = true;
105
106 SpecMainType.CanReturnFrontal = true;
107
108 SpecUnknownExt.CanReturnTail = true; // for sfx
109 SpecUnknownExt.CanReturnMid = true;
110 SpecUnknownExt.CanReturnFrontal = true; // for alt streams of sfx with pad
111
112 // ZerosTailIsAllowed = true;
113 }
114};
115
116struct COpenOptions
117{
118 CCodecs *codecs;
119 COpenType openType;
120 const CObjectVector<COpenType> *types;
121 const CIntVector *excludedFormats;
122
123 IInStream *stream;
124 ISequentialInStream *seqStream;
125 IArchiveOpenCallback *callback;
126 COpenCallbackImp *callbackSpec; // it's used for SFX only
127 OPEN_PROPS_DECL
128 // bool openOnlySpecifiedByExtension,
129
130 bool stdInMode;
131 UString filePath;
132
133 COpenOptions():
134 codecs(NULL),
135 types(NULL),
136 excludedFormats(NULL),
137 stream(NULL),
138 seqStream(NULL),
139 callback(NULL),
140 callbackSpec(NULL),
141 stdInMode(false)
142 {}
143
144};
145
146UInt32 GetOpenArcErrorFlags(const NWindows::NCOM::CPropVariant &prop, bool *isDefinedProp = NULL);
147
148struct CArcErrorInfo
149{
150 bool ThereIsTail;
151 bool UnexpecedEnd;
152 bool IgnoreTail; // all are zeros
153 // bool NonZerosTail;
154 bool ErrorFlags_Defined;
155 UInt32 ErrorFlags;
156 UInt32 WarningFlags;
157 int ErrorFormatIndex; // - 1 means no Error.
158 // if FormatIndex == ErrorFormatIndex, the archive is open with offset
159 UInt64 TailSize;
160
161 /* if CArc is Open OK with some format:
162 - ErrorFormatIndex shows error format index, if extension is incorrect
163 - other variables show message and warnings of archive that is open */
164
165 UString ErrorMessage;
166 UString WarningMessage;
167
168 // call IsArc_After_NonOpen only if Open returns S_FALSE
169 bool IsArc_After_NonOpen() const
170 {
171 return (ErrorFlags_Defined && (ErrorFlags & kpv_ErrorFlags_IsNotArc) == 0);
172 }
173
174
175 CArcErrorInfo():
176 ThereIsTail(false),
177 UnexpecedEnd(false),
178 IgnoreTail(false),
179 // NonZerosTail(false),
180 ErrorFlags_Defined(false),
181 ErrorFlags(0),
182 WarningFlags(0),
183 ErrorFormatIndex(-1),
184 TailSize(0)
185 {}
186
187 void ClearErrors();
188
189 void ClearErrors_Full()
190 {
191 ErrorFormatIndex = -1;
192 ClearErrors();
193 }
194
195 bool IsThereErrorOrWarning() const
196 {
197 return ErrorFlags != 0
198 || WarningFlags != 0
199 || NeedTailWarning()
200 || UnexpecedEnd
201 || !ErrorMessage.IsEmpty()
202 || !WarningMessage.IsEmpty();
203 }
204
205 bool AreThereErrors() const { return ErrorFlags != 0 || UnexpecedEnd; }
206 bool AreThereWarnings() const { return WarningFlags != 0 || NeedTailWarning(); }
207
208 bool NeedTailWarning() const { return !IgnoreTail && ThereIsTail; }
209
210 UInt32 GetWarningFlags() const
211 {
212 UInt32 a = WarningFlags;
213 if (NeedTailWarning() && (ErrorFlags & kpv_ErrorFlags_DataAfterEnd) == 0)
214 a |= kpv_ErrorFlags_DataAfterEnd;
215 return a;
216 }
217
218 UInt32 GetErrorFlags() const
219 {
220 UInt32 a = ErrorFlags;
221 if (UnexpecedEnd)
222 a |= kpv_ErrorFlags_UnexpectedEnd;
223 return a;
224 }
225};
226
227struct CReadArcItem
228{
229 UString Path; // Path from root (including alt stream name, if alt stream)
230 UStringVector PathParts; // without altStream name, path from root or from _baseParentFolder, if _use_baseParentFolder_mode
231
232 #ifdef SUPPORT_ALT_STREAMS
233 UString MainPath;
234 /* MainPath = Path for non-AltStream,
235 MainPath = Path of parent, if there is parent for AltStream. */
236 UString AltStreamName;
237 bool IsAltStream;
238 bool WriteToAltStreamIfColon;
239 #endif
240
241 bool IsDir;
242 bool MainIsDir;
243 UInt32 ParentIndex; // use it, if IsAltStream
244
245 #ifndef _SFX
246 bool _use_baseParentFolder_mode;
247 int _baseParentFolder;
248 #endif
249
250 CReadArcItem()
251 {
252 #ifdef SUPPORT_ALT_STREAMS
253 WriteToAltStreamIfColon = false;
254 #endif
255
256 #ifndef _SFX
257 _use_baseParentFolder_mode = false;
258 _baseParentFolder = -1;
259 #endif
260 }
261};
262
263class CArc
264{
265 HRESULT PrepareToOpen(const COpenOptions &op, unsigned formatIndex, CMyComPtr<IInArchive> &archive);
266 HRESULT CheckZerosTail(const COpenOptions &op, UInt64 offset);
267 HRESULT OpenStream2(const COpenOptions &options);
268
269 #ifndef _SFX
270 // parts.Back() can contain alt stream name "nams:AltName"
271 HRESULT GetItemPathToParent(UInt32 index, UInt32 parent, UStringVector &parts) const;
272 #endif
273
274public:
275 CMyComPtr<IInArchive> Archive;
276 CMyComPtr<IInStream> InStream;
277 // we use InStream in 2 cases (ArcStreamOffset != 0):
278 // 1) if we use additional cache stream
279 // 2) we reopen sfx archive with CTailInStream
280
281 CMyComPtr<IArchiveGetRawProps> GetRawProps;
282 CMyComPtr<IArchiveGetRootProps> GetRootProps;
283
284 CArcErrorInfo ErrorInfo; // for OK archives
285 CArcErrorInfo NonOpen_ErrorInfo; // ErrorInfo for mainArchive (false OPEN)
286
287 UString Path;
288 UString filePath;
289 UString DefaultName;
290 int FormatIndex; // -1 means Parser
291 UInt32 SubfileIndex; // (UInt32)(Int32)-1; means no subfile
292 FILETIME MTime;
293 bool MTimeDefined;
294
295 Int64 Offset; // it's offset of start of archive inside stream that is open by Archive Handler
296 UInt64 PhySize;
297 // UInt64 OkPhySize;
298 bool PhySizeDefined;
299 // bool OkPhySize_Defined;
300 UInt64 FileSize;
301 UInt64 AvailPhySize; // PhySize, but it's reduced if exceed end of file
302 // bool offsetDefined;
303
304 UInt64 GetEstmatedPhySize() const { return PhySizeDefined ? PhySize : FileSize; }
305
306 UInt64 ArcStreamOffset; // offset of stream that is open by Archive Handler
307 Int64 GetGlobalOffset() const { return (Int64)ArcStreamOffset + Offset; } // it's global offset of archive
308
309 // AString ErrorFlagsText;
310
311 bool IsParseArc;
312
313 bool IsTree;
314 bool IsReadOnly;
315
316 bool Ask_Deleted;
317 bool Ask_AltStream;
318 bool Ask_Aux;
319 bool Ask_INode;
320
321 bool IgnoreSplit; // don't try split handler
322
323 // void Set_ErrorFlagsText();
324
325 CArc():
326 MTimeDefined(false),
327 IsTree(false),
328 IsReadOnly(false),
329 Ask_Deleted(false),
330 Ask_AltStream(false),
331 Ask_Aux(false),
332 Ask_INode(false),
333 IgnoreSplit(false)
334 {}
335
336 HRESULT ReadBasicProps(IInArchive *archive, UInt64 startPos, HRESULT openRes);
337
338 // ~CArc();
339
340 HRESULT Close()
341 {
342 InStream.Release();
343 return Archive->Close();
344 }
345
346 HRESULT GetItemPath(UInt32 index, UString &result) const;
347 HRESULT GetDefaultItemPath(UInt32 index, UString &result) const;
348
349 // GetItemPath2 adds [DELETED] dir prefix for deleted items.
350 HRESULT GetItemPath2(UInt32 index, UString &result) const;
351
352 HRESULT GetItem(UInt32 index, CReadArcItem &item) const;
353
354 HRESULT GetItemSize(UInt32 index, UInt64 &size, bool &defined) const;
355 HRESULT GetItemMTime(UInt32 index, FILETIME &ft, bool &defined) const;
356 HRESULT IsItemAnti(UInt32 index, bool &result) const
357 { return Archive_GetItemBoolProp(Archive, index, kpidIsAnti, result); }
358
359
360 HRESULT OpenStream(const COpenOptions &options);
361 HRESULT OpenStreamOrFile(COpenOptions &options);
362
363 HRESULT ReOpen(const COpenOptions &options, IArchiveOpenCallback *openCallback_Additional);
364
365 HRESULT CreateNewTailStream(CMyComPtr<IInStream> &stream);
366
367 bool IsHashHandler(const COpenOptions &options) const
368 {
369 if (FormatIndex < 0)
370 return false;
371 return options.codecs->Formats[(unsigned)FormatIndex].Flags_HashHandler();
372 }
373};
374
375struct CArchiveLink
376{
377 CObjectVector<CArc> Arcs;
378 UStringVector VolumePaths;
379 UInt64 VolumesSize;
380 bool IsOpen;
381
382 bool PasswordWasAsked;
383 // UString Password;
384
385 // int NonOpenErrorFormatIndex; // - 1 means no Error.
386 UString NonOpen_ArcPath;
387
388 CArcErrorInfo NonOpen_ErrorInfo;
389
390 // UString ErrorsText;
391 // void Set_ErrorsText();
392
393 CArchiveLink():
394 VolumesSize(0),
395 IsOpen(false),
396 PasswordWasAsked(false)
397 {}
398
399 void KeepModeForNextOpen();
400 HRESULT Close();
401 void Release();
402 ~CArchiveLink() { Release(); }
403
404 const CArc *GetArc() const { return &Arcs.Back(); }
405 IInArchive *GetArchive() const { return Arcs.Back().Archive; }
406 IArchiveGetRawProps *GetArchiveGetRawProps() const { return Arcs.Back().GetRawProps; }
407 IArchiveGetRootProps *GetArchiveGetRootProps() const { return Arcs.Back().GetRootProps; }
408
409 /*
410 Open() opens archive and COpenOptions::callback
411 Open2() uses COpenCallbackImp that implements Volumes and password callback
412 Open3() calls Open2() and callbackUI->Open_Finished();
413 Open_Strict() returns S_FALSE also in case, if there is non-open expected nested archive.
414 */
415
416 HRESULT Open(COpenOptions &options);
417 HRESULT Open2(COpenOptions &options, IOpenCallbackUI *callbackUI);
418 HRESULT Open3(COpenOptions &options, IOpenCallbackUI *callbackUI);
419
420 HRESULT Open_Strict(COpenOptions &options, IOpenCallbackUI *callbackUI)
421 {
422 HRESULT result = Open3(options, callbackUI);
423 if (result == S_OK && NonOpen_ErrorInfo.ErrorFormatIndex >= 0)
424 result = S_FALSE;
425 return result;
426 }
427
428 HRESULT ReOpen(COpenOptions &options);
429};
430
431bool ParseOpenTypes(CCodecs &codecs, const UString &s, CObjectVector<COpenType> &types);
432
433// bool IsHashType(const CObjectVector<COpenType> &types);
434
435
436struct CDirPathSortPair
437{
438 unsigned Len;
439 unsigned Index;
440
441 void SetNumSlashes(const FChar *s);
442
443 int Compare(const CDirPathSortPair &a) const
444 {
445 // We need sorting order where parent items will be after child items
446 if (Len < a.Len) return 1;
447 if (Len > a.Len) return -1;
448 if (Index < a.Index) return -1;
449 if (Index > a.Index) return 1;
450 return 0;
451 }
452};
453
454#endif
diff --git a/CPP/7zip/UI/Common/PropIDUtils.cpp b/CPP/7zip/UI/Common/PropIDUtils.cpp
new file mode 100644
index 0000000..30efd53
--- /dev/null
+++ b/CPP/7zip/UI/Common/PropIDUtils.cpp
@@ -0,0 +1,696 @@
1// PropIDUtils.cpp
2
3#include "StdAfx.h"
4
5#include "../../../../C/CpuArch.h"
6
7#include "../../../Common/IntToString.h"
8#include "../../../Common/StringConvert.h"
9
10#include "../../../Windows/FileIO.h"
11#include "../../../Windows/PropVariantConv.h"
12
13#include "../../PropID.h"
14
15#include "PropIDUtils.h"
16
17#ifndef _SFX
18#define Get16(x) GetUi16(x)
19#define Get32(x) GetUi32(x)
20#endif
21
22using namespace NWindows;
23
24static const unsigned kNumWinAtrribFlags = 21;
25static const char g_WinAttribChars[kNumWinAtrribFlags + 1] = "RHS8DAdNTsLCOIEV.X.PU";
26
27/*
28FILE_ATTRIBUTE_
29
300 READONLY
311 HIDDEN
322 SYSTEM
333 (Volume label - obsolete)
344 DIRECTORY
355 ARCHIVE
366 DEVICE
377 NORMAL
388 TEMPORARY
399 SPARSE_FILE
4010 REPARSE_POINT
4111 COMPRESSED
4212 OFFLINE
4313 NOT_CONTENT_INDEXED (I - Win10 attrib/Explorer)
4414 ENCRYPTED
4515 INTEGRITY_STREAM (V - ReFS Win8/Win2012)
4616 VIRTUAL (reserved)
4717 NO_SCRUB_DATA (X - ReFS Win8/Win2012 attrib)
4818 RECALL_ON_OPEN or EA
4919 PINNED
5020 UNPINNED
5121 STRICTLY_SEQUENTIAL
5222 RECALL_ON_DATA_ACCESS
53*/
54
55
56static const char kPosixTypes[16] = { '0', 'p', 'c', '3', 'd', '5', 'b', '7', '-', '9', 'l', 'B', 's', 'D', 'E', 'F' };
57#define MY_ATTR_CHAR(a, n, c) ((a) & (1 << (n))) ? c : '-';
58
59static void ConvertPosixAttribToString(char *s, UInt32 a) throw()
60{
61 s[0] = kPosixTypes[(a >> 12) & 0xF];
62 for (int i = 6; i >= 0; i -= 3)
63 {
64 s[7 - i] = MY_ATTR_CHAR(a, i + 2, 'r');
65 s[8 - i] = MY_ATTR_CHAR(a, i + 1, 'w');
66 s[9 - i] = MY_ATTR_CHAR(a, i + 0, 'x');
67 }
68 if ((a & 0x800) != 0) s[3] = ((a & (1 << 6)) ? 's' : 'S'); // S_ISUID
69 if ((a & 0x400) != 0) s[6] = ((a & (1 << 3)) ? 's' : 'S'); // S_ISGID
70 if ((a & 0x200) != 0) s[9] = ((a & (1 << 0)) ? 't' : 'T'); // S_ISVTX
71 s[10] = 0;
72
73 a &= ~(UInt32)0xFFFF;
74 if (a != 0)
75 {
76 s[10] = ' ';
77 ConvertUInt32ToHex8Digits(a, s + 11);
78 }
79}
80
81
82void ConvertWinAttribToString(char *s, UInt32 wa) throw()
83{
84 /*
85 some programs store posix attributes in high 16 bits.
86 p7zip - stores additional 0x8000 flag marker.
87 macos - stores additional 0x4000 flag marker.
88 info-zip - no additional marker.
89 */
90
91 bool isPosix = ((wa & 0xF0000000) != 0);
92
93 UInt32 posix = 0;
94 if (isPosix)
95 {
96 posix = wa >> 16;
97 wa &= (UInt32)0x3FFF;
98 }
99
100 for (unsigned i = 0; i < kNumWinAtrribFlags; i++)
101 {
102 UInt32 flag = (1 << i);
103 if ((wa & flag) != 0)
104 {
105 char c = g_WinAttribChars[i];
106 if (c != '.')
107 {
108 wa &= ~flag;
109 // if (i != 7) // we can disable N (NORMAL) printing
110 *s++ = c;
111 }
112 }
113 }
114
115 if (wa != 0)
116 {
117 *s++ = ' ';
118 ConvertUInt32ToHex8Digits(wa, s);
119 s += strlen(s);
120 }
121
122 *s = 0;
123
124 if (isPosix)
125 {
126 *s++ = ' ';
127 ConvertPosixAttribToString(s, posix);
128 }
129}
130
131
132void ConvertPropertyToShortString2(char *dest, const PROPVARIANT &prop, PROPID propID, int level) throw()
133{
134 *dest = 0;
135
136 if (prop.vt == VT_FILETIME)
137 {
138 const FILETIME &ft = prop.filetime;
139 if ((ft.dwHighDateTime == 0 &&
140 ft.dwLowDateTime == 0))
141 return;
142 ConvertUtcFileTimeToString(prop.filetime, dest, level);
143 return;
144 }
145
146 switch (propID)
147 {
148 case kpidCRC:
149 {
150 if (prop.vt != VT_UI4)
151 break;
152 ConvertUInt32ToHex8Digits(prop.ulVal, dest);
153 return;
154 }
155 case kpidAttrib:
156 {
157 if (prop.vt != VT_UI4)
158 break;
159 UInt32 a = prop.ulVal;
160
161 /*
162 if ((a & 0x8000) && (a & 0x7FFF) == 0)
163 ConvertPosixAttribToString(dest, a >> 16);
164 else
165 */
166 ConvertWinAttribToString(dest, a);
167 return;
168 }
169 case kpidPosixAttrib:
170 {
171 if (prop.vt != VT_UI4)
172 break;
173 ConvertPosixAttribToString(dest, prop.ulVal);
174 return;
175 }
176 case kpidINode:
177 {
178 if (prop.vt != VT_UI8)
179 break;
180 ConvertUInt32ToString((UInt32)(prop.uhVal.QuadPart >> 48), dest);
181 dest += strlen(dest);
182 *dest++ = '-';
183 UInt64 low = prop.uhVal.QuadPart & (((UInt64)1 << 48) - 1);
184 ConvertUInt64ToString(low, dest);
185 return;
186 }
187 case kpidVa:
188 {
189 UInt64 v = 0;
190 if (prop.vt == VT_UI4)
191 v = prop.ulVal;
192 else if (prop.vt == VT_UI8)
193 v = (UInt64)prop.uhVal.QuadPart;
194 else
195 break;
196 dest[0] = '0';
197 dest[1] = 'x';
198 ConvertUInt64ToHex(v, dest + 2);
199 return;
200 }
201 }
202
203 ConvertPropVariantToShortString(prop, dest);
204}
205
206void ConvertPropertyToString2(UString &dest, const PROPVARIANT &prop, PROPID propID, int level)
207{
208 if (prop.vt == VT_BSTR)
209 {
210 dest.SetFromBstr(prop.bstrVal);
211 return;
212 }
213 char temp[64];
214 ConvertPropertyToShortString2(temp, prop, propID, level);
215 dest = temp;
216}
217
218#ifndef _SFX
219
220static inline unsigned GetHex(unsigned v)
221{
222 return (v < 10) ? ('0' + v) : ('A' + (v - 10));
223}
224
225static inline void AddHexToString(AString &res, unsigned v)
226{
227 res += (char)GetHex(v >> 4);
228 res += (char)GetHex(v & 0xF);
229}
230
231/*
232static AString Data_To_Hex(const Byte *data, size_t size)
233{
234 AString s;
235 for (size_t i = 0; i < size; i++)
236 AddHexToString(s, data[i]);
237 return s;
238}
239*/
240
241static const char * const sidNames[] =
242{
243 "0"
244 , "Dialup"
245 , "Network"
246 , "Batch"
247 , "Interactive"
248 , "Logon" // S-1-5-5-X-Y
249 , "Service"
250 , "Anonymous"
251 , "Proxy"
252 , "EnterpriseDC"
253 , "Self"
254 , "AuthenticatedUsers"
255 , "RestrictedCode"
256 , "TerminalServer"
257 , "RemoteInteractiveLogon"
258 , "ThisOrganization"
259 , "16"
260 , "IUserIIS"
261 , "LocalSystem"
262 , "LocalService"
263 , "NetworkService"
264 , "Domains"
265};
266
267struct CSecID2Name
268{
269 UInt32 n;
270 const char *sz;
271};
272
273static int FindPairIndex(const CSecID2Name * pairs, unsigned num, UInt32 id)
274{
275 for (unsigned i = 0; i < num; i++)
276 if (pairs[i].n == id)
277 return (int)i;
278 return -1;
279}
280
281static const CSecID2Name sid_32_Names[] =
282{
283 { 544, "Administrators" },
284 { 545, "Users" },
285 { 546, "Guests" },
286 { 547, "PowerUsers" },
287 { 548, "AccountOperators" },
288 { 549, "ServerOperators" },
289 { 550, "PrintOperators" },
290 { 551, "BackupOperators" },
291 { 552, "Replicators" },
292 { 553, "Backup Operators" },
293 { 554, "PreWindows2000CompatibleAccess" },
294 { 555, "RemoteDesktopUsers" },
295 { 556, "NetworkConfigurationOperators" },
296 { 557, "IncomingForestTrustBuilders" },
297 { 558, "PerformanceMonitorUsers" },
298 { 559, "PerformanceLogUsers" },
299 { 560, "WindowsAuthorizationAccessGroup" },
300 { 561, "TerminalServerLicenseServers" },
301 { 562, "DistributedCOMUsers" },
302 { 569, "CryptographicOperators" },
303 { 573, "EventLogReaders" },
304 { 574, "CertificateServiceDCOMAccess" }
305};
306
307static const CSecID2Name sid_21_Names[] =
308{
309 { 500, "Administrator" },
310 { 501, "Guest" },
311 { 502, "KRBTGT" },
312 { 512, "DomainAdmins" },
313 { 513, "DomainUsers" },
314 { 515, "DomainComputers" },
315 { 516, "DomainControllers" },
316 { 517, "CertPublishers" },
317 { 518, "SchemaAdmins" },
318 { 519, "EnterpriseAdmins" },
319 { 520, "GroupPolicyCreatorOwners" },
320 { 553, "RASandIASServers" },
321 { 553, "RASandIASServers" },
322 { 571, "AllowedRODCPasswordReplicationGroup" },
323 { 572, "DeniedRODCPasswordReplicationGroup" }
324};
325
326struct CServicesToName
327{
328 UInt32 n[5];
329 const char *sz;
330};
331
332static const CServicesToName services_to_name[] =
333{
334 { { 0x38FB89B5, 0xCBC28419, 0x6D236C5C, 0x6E770057, 0x876402C0 } , "TrustedInstaller" }
335};
336
337static void ParseSid(AString &s, const Byte *p, UInt32 lim, UInt32 &sidSize)
338{
339 sidSize = 0;
340 if (lim < 8)
341 {
342 s += "ERROR";
343 return;
344 }
345 UInt32 rev = p[0];
346 if (rev != 1)
347 {
348 s += "UNSUPPORTED";
349 return;
350 }
351 UInt32 num = p[1];
352 if (8 + num * 4 > lim)
353 {
354 s += "ERROR";
355 return;
356 }
357 sidSize = 8 + num * 4;
358 UInt32 authority = GetBe32(p + 4);
359
360 if (p[2] == 0 && p[3] == 0 && authority == 5 && num >= 1)
361 {
362 UInt32 v0 = Get32(p + 8);
363 if (v0 < ARRAY_SIZE(sidNames))
364 {
365 s += sidNames[v0];
366 return;
367 }
368 if (v0 == 32 && num == 2)
369 {
370 UInt32 v1 = Get32(p + 12);
371 int index = FindPairIndex(sid_32_Names, ARRAY_SIZE(sid_32_Names), v1);
372 if (index >= 0)
373 {
374 s += sid_32_Names[(unsigned)index].sz;
375 return;
376 }
377 }
378 if (v0 == 21 && num == 5)
379 {
380 UInt32 v4 = Get32(p + 8 + 4 * 4);
381 int index = FindPairIndex(sid_21_Names, ARRAY_SIZE(sid_21_Names), v4);
382 if (index >= 0)
383 {
384 s += sid_21_Names[(unsigned)index].sz;
385 return;
386 }
387 }
388 if (v0 == 80 && num == 6)
389 {
390 for (unsigned i = 0; i < ARRAY_SIZE(services_to_name); i++)
391 {
392 const CServicesToName &sn = services_to_name[i];
393 int j;
394 for (j = 0; j < 5 && sn.n[j] == Get32(p + 8 + 4 + j * 4); j++);
395 if (j == 5)
396 {
397 s += sn.sz;
398 return;
399 }
400 }
401 }
402 }
403
404 s += "S-1-";
405 if (p[2] == 0 && p[3] == 0)
406 s.Add_UInt32(authority);
407 else
408 {
409 s += "0x";
410 for (int i = 2; i < 8; i++)
411 AddHexToString(s, p[i]);
412 }
413 for (UInt32 i = 0; i < num; i++)
414 {
415 s += '-';
416 s.Add_UInt32(Get32(p + 8 + i * 4));
417 }
418}
419
420static void ParseOwner(AString &s, const Byte *p, UInt32 size, UInt32 pos)
421{
422 if (pos > size)
423 {
424 s += "ERROR";
425 return;
426 }
427 UInt32 sidSize = 0;
428 ParseSid(s, p + pos, size - pos, sidSize);
429}
430
431static void ParseAcl(AString &s, const Byte *p, UInt32 size, const char *strName, UInt32 flags, UInt32 offset)
432{
433 UInt32 control = Get16(p + 2);
434 if ((flags & control) == 0)
435 return;
436 UInt32 pos = Get32(p + offset);
437 s.Add_Space();
438 s += strName;
439 if (pos >= size)
440 return;
441 p += pos;
442 size -= pos;
443 if (size < 8)
444 return;
445 if (Get16(p) != 2) // revision
446 return;
447 UInt32 num = Get32(p + 4);
448 s.Add_UInt32(num);
449
450 /*
451 UInt32 aclSize = Get16(p + 2);
452 if (num >= (1 << 16))
453 return;
454 if (aclSize > size)
455 return;
456 size = aclSize;
457 size -= 8;
458 p += 8;
459 for (UInt32 i = 0 ; i < num; i++)
460 {
461 if (size <= 8)
462 return;
463 // Byte type = p[0];
464 // Byte flags = p[1];
465 // UInt32 aceSize = Get16(p + 2);
466 // UInt32 mask = Get32(p + 4);
467 p += 8;
468 size -= 8;
469
470 UInt32 sidSize = 0;
471 s.Add_Space();
472 ParseSid(s, p, size, sidSize);
473 if (sidSize == 0)
474 return;
475 p += sidSize;
476 size -= sidSize;
477 }
478
479 // the tail can contain zeros. So (size != 0) is not ERROR
480 // if (size != 0) s += " ERROR";
481 */
482}
483
484/*
485#define MY_SE_OWNER_DEFAULTED (0x0001)
486#define MY_SE_GROUP_DEFAULTED (0x0002)
487*/
488#define MY_SE_DACL_PRESENT (0x0004)
489/*
490#define MY_SE_DACL_DEFAULTED (0x0008)
491*/
492#define MY_SE_SACL_PRESENT (0x0010)
493/*
494#define MY_SE_SACL_DEFAULTED (0x0020)
495#define MY_SE_DACL_AUTO_INHERIT_REQ (0x0100)
496#define MY_SE_SACL_AUTO_INHERIT_REQ (0x0200)
497#define MY_SE_DACL_AUTO_INHERITED (0x0400)
498#define MY_SE_SACL_AUTO_INHERITED (0x0800)
499#define MY_SE_DACL_PROTECTED (0x1000)
500#define MY_SE_SACL_PROTECTED (0x2000)
501#define MY_SE_RM_CONTROL_VALID (0x4000)
502#define MY_SE_SELF_RELATIVE (0x8000)
503*/
504
505void ConvertNtSecureToString(const Byte *data, UInt32 size, AString &s)
506{
507 s.Empty();
508 if (size < 20 || size > (1 << 18))
509 {
510 s += "ERROR";
511 return;
512 }
513 if (Get16(data) != 1) // revision
514 {
515 s += "UNSUPPORTED";
516 return;
517 }
518 ParseOwner(s, data, size, Get32(data + 4));
519 s.Add_Space();
520 ParseOwner(s, data, size, Get32(data + 8));
521 ParseAcl(s, data, size, "s:", MY_SE_SACL_PRESENT, 12);
522 ParseAcl(s, data, size, "d:", MY_SE_DACL_PRESENT, 16);
523 s.Add_Space();
524 s.Add_UInt32(size);
525 // s += '\n';
526 // s += Data_To_Hex(data, size);
527}
528
529#ifdef _WIN32
530
531static bool CheckSid(const Byte *data, UInt32 size, UInt32 pos) throw()
532{
533 if (pos >= size)
534 return false;
535 size -= pos;
536 if (size < 8)
537 return false;
538 UInt32 rev = data[pos];
539 if (rev != 1)
540 return false;
541 UInt32 num = data[pos + 1];
542 return (8 + num * 4 <= size);
543}
544
545static bool CheckAcl(const Byte *p, UInt32 size, UInt32 flags, UInt32 offset) throw()
546{
547 UInt32 control = Get16(p + 2);
548 if ((flags & control) == 0)
549 return true;
550 UInt32 pos = Get32(p + offset);
551 if (pos >= size)
552 return false;
553 p += pos;
554 size -= pos;
555 if (size < 8)
556 return false;
557 UInt32 aclSize = Get16(p + 2);
558 return (aclSize <= size);
559}
560
561bool CheckNtSecure(const Byte *data, UInt32 size) throw()
562{
563 if (size < 20)
564 return false;
565 if (Get16(data) != 1) // revision
566 return true; // windows function can handle such error, so we allow it
567 if (size > (1 << 18))
568 return false;
569 if (!CheckSid(data, size, Get32(data + 4))) return false;
570 if (!CheckSid(data, size, Get32(data + 8))) return false;
571 if (!CheckAcl(data, size, MY_SE_SACL_PRESENT, 12)) return false;
572 if (!CheckAcl(data, size, MY_SE_DACL_PRESENT, 16)) return false;
573 return true;
574}
575
576#endif
577
578
579
580// IO_REPARSE_TAG_*
581
582static const CSecID2Name k_ReparseTags[] =
583{
584 { 0xA0000003, "MOUNT_POINT" },
585 { 0xC0000004, "HSM" },
586 { 0x80000005, "DRIVE_EXTENDER" },
587 { 0x80000006, "HSM2" },
588 { 0x80000007, "SIS" },
589 { 0x80000008, "WIM" },
590 { 0x80000009, "CSV" },
591 { 0x8000000A, "DFS" },
592 { 0x8000000B, "FILTER_MANAGER" },
593 { 0xA000000C, "SYMLINK" },
594 { 0xA0000010, "IIS_CACHE" },
595 { 0x80000012, "DFSR" },
596 { 0x80000013, "DEDUP" },
597 { 0xC0000014, "APPXSTRM" },
598 { 0x80000014, "NFS" },
599 { 0x80000015, "FILE_PLACEHOLDER" },
600 { 0x80000016, "DFM" },
601 { 0x80000017, "WOF" },
602 { 0x80000018, "WCI" },
603 { 0x8000001B, "APPEXECLINK" },
604 { 0xA000001D, "LX_SYMLINK" },
605 { 0x80000023, "AF_UNIX" },
606 { 0x80000024, "LX_FIFO" },
607 { 0x80000025, "LX_CHR" },
608 { 0x80000026, "LX_BLK" }
609};
610
611bool ConvertNtReparseToString(const Byte *data, UInt32 size, UString &s)
612{
613 s.Empty();
614 NFile::CReparseAttr attr;
615
616 if (attr.Parse(data, size))
617 {
618 if (attr.IsSymLink_WSL())
619 {
620 s += "WSL: ";
621 s += attr.GetPath();
622 }
623 else
624 {
625 if (!attr.IsSymLink_Win())
626 s += "Junction: ";
627 s += attr.GetPath();
628 if (s.IsEmpty())
629 s += "Link: ";
630 if (!attr.IsOkNamePair())
631 {
632 s += " : ";
633 s += attr.PrintName;
634 }
635 }
636 if (attr.MinorError)
637 s += " : MINOR_ERROR";
638 return true;
639 // s += " "; // for debug
640 }
641
642 if (size < 8)
643 return false;
644 UInt32 tag = Get32(data);
645 UInt32 len = Get16(data + 4);
646 if (len + 8 > size)
647 return false;
648 if (Get16(data + 6) != 0) // padding
649 return false;
650
651 /*
652 #define _my_IO_REPARSE_TAG_DEDUP (0x80000013L)
653 if (tag == _my_IO_REPARSE_TAG_DEDUP)
654 {
655 }
656 */
657
658 {
659 int index = FindPairIndex(k_ReparseTags, ARRAY_SIZE(k_ReparseTags), tag);
660 if (index >= 0)
661 s += k_ReparseTags[(unsigned)index].sz;
662 else
663 {
664 s += "REPARSE:";
665 char hex[16];
666 ConvertUInt32ToHex8Digits(tag, hex);
667 s += hex;
668 }
669 }
670
671 s += ":";
672 s.Add_UInt32(len);
673
674 if (len != 0)
675 {
676 s.Add_Space();
677
678 data += 8;
679
680 for (UInt32 i = 0; i < len; i++)
681 {
682 if (i >= 16)
683 {
684 s += "...";
685 break;
686 }
687 unsigned b = data[i];
688 s += (char)GetHex((b >> 4) & 0xF);
689 s += (char)GetHex(b & 0xF);
690 }
691 }
692
693 return true;
694}
695
696#endif
diff --git a/CPP/7zip/UI/Common/PropIDUtils.h b/CPP/7zip/UI/Common/PropIDUtils.h
new file mode 100644
index 0000000..915bfc2
--- /dev/null
+++ b/CPP/7zip/UI/Common/PropIDUtils.h
@@ -0,0 +1,18 @@
1// PropIDUtils.h
2
3#ifndef __PROPID_UTILS_H
4#define __PROPID_UTILS_H
5
6#include "../../../Common/MyString.h"
7
8// provide at least 64 bytes for buffer including zero-end
9void ConvertPropertyToShortString2(char *dest, const PROPVARIANT &propVariant, PROPID propID, int level = 0) throw();
10void ConvertPropertyToString2(UString &dest, const PROPVARIANT &propVariant, PROPID propID, int level = 0);
11
12bool ConvertNtReparseToString(const Byte *data, UInt32 size, UString &s);
13void ConvertNtSecureToString(const Byte *data, UInt32 size, AString &s);
14bool CheckNtSecure(const Byte *data, UInt32 size) throw();;
15
16void ConvertWinAttribToString(char *s, UInt32 wa) throw();
17
18#endif
diff --git a/CPP/7zip/UI/Common/Property.h b/CPP/7zip/UI/Common/Property.h
new file mode 100644
index 0000000..8b57a2a
--- /dev/null
+++ b/CPP/7zip/UI/Common/Property.h
@@ -0,0 +1,14 @@
1// Property.h
2
3#ifndef __7Z_PROPERTY_H
4#define __7Z_PROPERTY_H
5
6#include "../../../Common/MyString.h"
7
8struct CProperty
9{
10 UString Name;
11 UString Value;
12};
13
14#endif
diff --git a/CPP/7zip/UI/Common/SetProperties.cpp b/CPP/7zip/UI/Common/SetProperties.cpp
new file mode 100644
index 0000000..4b3037a
--- /dev/null
+++ b/CPP/7zip/UI/Common/SetProperties.cpp
@@ -0,0 +1,87 @@
1// SetProperties.cpp
2
3#include "StdAfx.h"
4
5#include "../../../Common/MyCom.h"
6#include "../../../Common/MyString.h"
7#include "../../../Common/StringToInt.h"
8
9#include "../../../Windows/PropVariant.h"
10
11#include "../../Archive/IArchive.h"
12
13#include "SetProperties.h"
14
15using namespace NWindows;
16using namespace NCOM;
17
18static void ParseNumberString(const UString &s, NCOM::CPropVariant &prop)
19{
20 const wchar_t *end;
21 UInt64 result = ConvertStringToUInt64(s, &end);
22 if (*end != 0 || s.IsEmpty())
23 prop = s;
24 else if (result <= (UInt32)0xFFFFFFFF)
25 prop = (UInt32)result;
26 else
27 prop = result;
28}
29
30
31struct CPropPropetiesVector
32{
33 CPropVariant *values;
34 CPropPropetiesVector(unsigned num)
35 {
36 values = new CPropVariant[num];
37 }
38 ~CPropPropetiesVector()
39 {
40 delete []values;
41 }
42};
43
44
45HRESULT SetProperties(IUnknown *unknown, const CObjectVector<CProperty> &properties)
46{
47 if (properties.IsEmpty())
48 return S_OK;
49 CMyComPtr<ISetProperties> setProperties;
50 unknown->QueryInterface(IID_ISetProperties, (void **)&setProperties);
51 if (!setProperties)
52 return S_OK;
53
54 UStringVector realNames;
55 CPropPropetiesVector values(properties.Size());
56 {
57 unsigned i;
58 for (i = 0; i < properties.Size(); i++)
59 {
60 const CProperty &property = properties[i];
61 NCOM::CPropVariant propVariant;
62 UString name = property.Name;
63 if (property.Value.IsEmpty())
64 {
65 if (!name.IsEmpty())
66 {
67 wchar_t c = name.Back();
68 if (c == L'-')
69 propVariant = false;
70 else if (c == L'+')
71 propVariant = true;
72 if (propVariant.vt != VT_EMPTY)
73 name.DeleteBack();
74 }
75 }
76 else
77 ParseNumberString(property.Value, propVariant);
78 realNames.Add(name);
79 values.values[i] = propVariant;
80 }
81 CRecordVector<const wchar_t *> names;
82 for (i = 0; i < realNames.Size(); i++)
83 names.Add((const wchar_t *)realNames[i]);
84
85 return setProperties->SetProperties(&names.Front(), values.values, names.Size());
86 }
87}
diff --git a/CPP/7zip/UI/Common/SetProperties.h b/CPP/7zip/UI/Common/SetProperties.h
new file mode 100644
index 0000000..892f1a2
--- /dev/null
+++ b/CPP/7zip/UI/Common/SetProperties.h
@@ -0,0 +1,10 @@
1// SetProperties.h
2
3#ifndef __SETPROPERTIES_H
4#define __SETPROPERTIES_H
5
6#include "Property.h"
7
8HRESULT SetProperties(IUnknown *unknown, const CObjectVector<CProperty> &properties);
9
10#endif
diff --git a/CPP/7zip/UI/Common/SortUtils.cpp b/CPP/7zip/UI/Common/SortUtils.cpp
new file mode 100644
index 0000000..5f29249
--- /dev/null
+++ b/CPP/7zip/UI/Common/SortUtils.cpp
@@ -0,0 +1,25 @@
1// SortUtils.cpp
2
3#include "StdAfx.h"
4
5#include "../../../Common/Wildcard.h"
6
7#include "SortUtils.h"
8
9static int CompareStrings(const unsigned *p1, const unsigned *p2, void *param)
10{
11 const UStringVector &strings = *(const UStringVector *)param;
12 return CompareFileNames(strings[*p1], strings[*p2]);
13}
14
15void SortFileNames(const UStringVector &strings, CUIntVector &indices)
16{
17 const unsigned numItems = strings.Size();
18 indices.ClearAndSetSize(numItems);
19 if (numItems == 0)
20 return;
21 unsigned *vals = &indices[0];
22 for (unsigned i = 0; i < numItems; i++)
23 vals[i] = i;
24 indices.Sort(CompareStrings, (void *)&strings);
25}
diff --git a/CPP/7zip/UI/Common/SortUtils.h b/CPP/7zip/UI/Common/SortUtils.h
new file mode 100644
index 0000000..8e42e06
--- /dev/null
+++ b/CPP/7zip/UI/Common/SortUtils.h
@@ -0,0 +1,10 @@
1// SortUtils.h
2
3#ifndef __SORT_UTLS_H
4#define __SORT_UTLS_H
5
6#include "../../../Common/MyString.h"
7
8void SortFileNames(const UStringVector &strings, CUIntVector &indices);
9
10#endif
diff --git a/CPP/7zip/UI/Common/StdAfx.h b/CPP/7zip/UI/Common/StdAfx.h
new file mode 100644
index 0000000..2854ff3
--- /dev/null
+++ b/CPP/7zip/UI/Common/StdAfx.h
@@ -0,0 +1,8 @@
1// StdAfx.h
2
3#ifndef __STDAFX_H
4#define __STDAFX_H
5
6#include "../../../Common/Common.h"
7
8#endif
diff --git a/CPP/7zip/UI/Common/TempFiles.cpp b/CPP/7zip/UI/Common/TempFiles.cpp
new file mode 100644
index 0000000..2f86838
--- /dev/null
+++ b/CPP/7zip/UI/Common/TempFiles.cpp
@@ -0,0 +1,19 @@
1// TempFiles.cpp
2
3#include "StdAfx.h"
4
5#include "../../../Windows/FileDir.h"
6
7#include "TempFiles.h"
8
9using namespace NWindows;
10using namespace NFile;
11
12void CTempFiles::Clear()
13{
14 while (!Paths.IsEmpty())
15 {
16 NDir::DeleteFileAlways(Paths.Back());
17 Paths.DeleteBack();
18 }
19}
diff --git a/CPP/7zip/UI/Common/TempFiles.h b/CPP/7zip/UI/Common/TempFiles.h
new file mode 100644
index 0000000..4099e65
--- /dev/null
+++ b/CPP/7zip/UI/Common/TempFiles.h
@@ -0,0 +1,16 @@
1// TempFiles.h
2
3#ifndef __TEMP_FILES_H
4#define __TEMP_FILES_H
5
6#include "../../../Common/MyString.h"
7
8class CTempFiles
9{
10 void Clear();
11public:
12 FStringVector Paths;
13 ~CTempFiles() { Clear(); }
14};
15
16#endif
diff --git a/CPP/7zip/UI/Common/Update.cpp b/CPP/7zip/UI/Common/Update.cpp
new file mode 100644
index 0000000..032a876
--- /dev/null
+++ b/CPP/7zip/UI/Common/Update.cpp
@@ -0,0 +1,1763 @@
1// Update.cpp
2
3#include "StdAfx.h"
4
5#include "Update.h"
6
7#include "../../../Common/StringConvert.h"
8
9#include "../../../Windows/DLL.h"
10#include "../../../Windows/FileDir.h"
11#include "../../../Windows/FileFind.h"
12#include "../../../Windows/FileName.h"
13#include "../../../Windows/PropVariant.h"
14#include "../../../Windows/PropVariantConv.h"
15#include "../../../Windows/TimeUtils.h"
16
17#include "../../Common/FileStreams.h"
18#include "../../Common/LimitedStreams.h"
19
20#include "../../Compress/CopyCoder.h"
21
22#include "../Common/DirItem.h"
23#include "../Common/EnumDirItems.h"
24#include "../Common/OpenArchive.h"
25#include "../Common/UpdateProduce.h"
26
27#include "EnumDirItems.h"
28#include "SetProperties.h"
29#include "TempFiles.h"
30#include "UpdateCallback.h"
31
32static const char * const kUpdateIsNotSupoorted =
33 "update operations are not supported for this archive";
34
35static const char * const kUpdateIsNotSupported_MultiVol =
36 "Updating for multivolume archives is not implemented";
37
38using namespace NWindows;
39using namespace NCOM;
40using namespace NFile;
41using namespace NDir;
42using namespace NName;
43
44#ifdef _WIN32
45static CFSTR const kTempFolderPrefix = FTEXT("7zE");
46#endif
47
48void CUpdateErrorInfo::SetFromLastError(const char *message)
49{
50 SystemError = ::GetLastError();
51 Message = message;
52}
53
54HRESULT CUpdateErrorInfo::SetFromLastError(const char *message, const FString &fileName)
55{
56 SetFromLastError(message);
57 FileNames.Add(fileName);
58 return Get_HRESULT_Error();
59}
60
61HRESULT CUpdateErrorInfo::SetFromError_DWORD(const char *message, const FString &fileName, DWORD error)
62{
63 Message = message;
64 FileNames.Add(fileName);
65 SystemError = error;
66 return Get_HRESULT_Error();
67}
68
69
70using namespace NUpdateArchive;
71
72class COutMultiVolStream:
73 public IOutStream,
74 public CMyUnknownImp
75{
76 unsigned _streamIndex; // required stream
77 UInt64 _offsetPos; // offset from start of _streamIndex index
78 UInt64 _absPos;
79 UInt64 _length;
80
81 struct CAltStreamInfo
82 {
83 COutFileStream *StreamSpec;
84 CMyComPtr<IOutStream> Stream;
85 FString Name;
86 UInt64 Pos;
87 UInt64 RealSize;
88 };
89 CObjectVector<CAltStreamInfo> Streams;
90public:
91 // CMyComPtr<IArchiveUpdateCallback2> VolumeCallback;
92 CRecordVector<UInt64> Sizes;
93 FString Prefix;
94 CTempFiles *TempFiles;
95
96 void Init()
97 {
98 _streamIndex = 0;
99 _offsetPos = 0;
100 _absPos = 0;
101 _length = 0;
102 }
103
104 bool SetMTime(const FILETIME *mTime);
105 HRESULT Close();
106
107 UInt64 GetSize() const { return _length; }
108
109 MY_UNKNOWN_IMP1(IOutStream)
110
111 STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
112 STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
113 STDMETHOD(SetSize)(UInt64 newSize);
114};
115
116// static NSynchronization::CCriticalSection g_TempPathsCS;
117
118HRESULT COutMultiVolStream::Close()
119{
120 HRESULT res = S_OK;
121 FOR_VECTOR (i, Streams)
122 {
123 COutFileStream *s = Streams[i].StreamSpec;
124 if (s)
125 {
126 HRESULT res2 = s->Close();
127 if (res2 != S_OK)
128 res = res2;
129 }
130 }
131 return res;
132}
133
134bool COutMultiVolStream::SetMTime(const FILETIME *mTime)
135{
136 bool res = true;
137 FOR_VECTOR (i, Streams)
138 {
139 COutFileStream *s = Streams[i].StreamSpec;
140 if (s)
141 if (!s->SetMTime(mTime))
142 res = false;
143 }
144 return res;
145}
146
147STDMETHODIMP COutMultiVolStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
148{
149 if (processedSize)
150 *processedSize = 0;
151 while (size > 0)
152 {
153 if (_streamIndex >= Streams.Size())
154 {
155 CAltStreamInfo altStream;
156
157 FString name;
158 name.Add_UInt32(_streamIndex + 1);
159 while (name.Len() < 3)
160 name.InsertAtFront(FTEXT('0'));
161 name.Insert(0, Prefix);
162 altStream.StreamSpec = new COutFileStream;
163 altStream.Stream = altStream.StreamSpec;
164 if (!altStream.StreamSpec->Create(name, false))
165 return GetLastError_noZero_HRESULT();
166 {
167 // NSynchronization::CCriticalSectionLock lock(g_TempPathsCS);
168 TempFiles->Paths.Add(name);
169 }
170
171 altStream.Pos = 0;
172 altStream.RealSize = 0;
173 altStream.Name = name;
174 Streams.Add(altStream);
175 continue;
176 }
177 CAltStreamInfo &altStream = Streams[_streamIndex];
178
179 unsigned index = _streamIndex;
180 if (index >= Sizes.Size())
181 index = Sizes.Size() - 1;
182 UInt64 volSize = Sizes[index];
183
184 if (_offsetPos >= volSize)
185 {
186 _offsetPos -= volSize;
187 _streamIndex++;
188 continue;
189 }
190 if (_offsetPos != altStream.Pos)
191 {
192 // CMyComPtr<IOutStream> outStream;
193 // RINOK(altStream.Stream.QueryInterface(IID_IOutStream, &outStream));
194 RINOK(altStream.Stream->Seek((Int64)_offsetPos, STREAM_SEEK_SET, NULL));
195 altStream.Pos = _offsetPos;
196 }
197
198 UInt32 curSize = (UInt32)MyMin((UInt64)size, volSize - altStream.Pos);
199 UInt32 realProcessed;
200 RINOK(altStream.Stream->Write(data, curSize, &realProcessed));
201 data = (const void *)((const Byte *)data + realProcessed);
202 size -= realProcessed;
203 altStream.Pos += realProcessed;
204 _offsetPos += realProcessed;
205 _absPos += realProcessed;
206 if (_absPos > _length)
207 _length = _absPos;
208 if (_offsetPos > altStream.RealSize)
209 altStream.RealSize = _offsetPos;
210 if (processedSize)
211 *processedSize += realProcessed;
212 if (altStream.Pos == volSize)
213 {
214 _streamIndex++;
215 _offsetPos = 0;
216 }
217 if (realProcessed == 0 && curSize != 0)
218 return E_FAIL;
219 break;
220 }
221 return S_OK;
222}
223
224STDMETHODIMP COutMultiVolStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
225{
226 if (seekOrigin >= 3)
227 return STG_E_INVALIDFUNCTION;
228 switch (seekOrigin)
229 {
230 case STREAM_SEEK_SET: _absPos = (UInt64)offset; break;
231 case STREAM_SEEK_CUR: _absPos = (UInt64)((Int64)_absPos + offset); break;
232 case STREAM_SEEK_END: _absPos = (UInt64)((Int64)_length + offset); break;
233 }
234 _offsetPos = _absPos;
235 if (newPosition)
236 *newPosition = _absPos;
237 _streamIndex = 0;
238 return S_OK;
239}
240
241STDMETHODIMP COutMultiVolStream::SetSize(UInt64 newSize)
242{
243 unsigned i = 0;
244 while (i < Streams.Size())
245 {
246 CAltStreamInfo &altStream = Streams[i++];
247 if ((UInt64)newSize < altStream.RealSize)
248 {
249 RINOK(altStream.Stream->SetSize(newSize));
250 altStream.RealSize = newSize;
251 break;
252 }
253 newSize -= altStream.RealSize;
254 }
255 while (i < Streams.Size())
256 {
257 {
258 CAltStreamInfo &altStream = Streams.Back();
259 altStream.Stream.Release();
260 DeleteFileAlways(altStream.Name);
261 }
262 Streams.DeleteBack();
263 }
264 _offsetPos = _absPos;
265 _streamIndex = 0;
266 _length = newSize;
267 return S_OK;
268}
269
270void CArchivePath::ParseFromPath(const UString &path, EArcNameMode mode)
271{
272 OriginalPath = path;
273
274 SplitPathToParts_2(path, Prefix, Name);
275
276 if (mode == k_ArcNameMode_Add)
277 return;
278
279 if (mode != k_ArcNameMode_Exact)
280 {
281 int dotPos = Name.ReverseFind_Dot();
282 if (dotPos < 0)
283 return;
284 if ((unsigned)dotPos == Name.Len() - 1)
285 Name.DeleteBack();
286 else
287 {
288 const UString ext = Name.Ptr((unsigned)(dotPos + 1));
289 if (BaseExtension.IsEqualTo_NoCase(ext))
290 {
291 BaseExtension = ext;
292 Name.DeleteFrom((unsigned)dotPos);
293 return;
294 }
295 }
296 }
297
298 BaseExtension.Empty();
299}
300
301UString CArchivePath::GetFinalPath() const
302{
303 UString path = GetPathWithoutExt();
304 if (!BaseExtension.IsEmpty())
305 {
306 path += '.';
307 path += BaseExtension;
308 }
309 return path;
310}
311
312UString CArchivePath::GetFinalVolPath() const
313{
314 UString path = GetPathWithoutExt();
315 // if BaseExtension is empty, we must ignore VolExtension also.
316 if (!BaseExtension.IsEmpty())
317 {
318 path += '.';
319 path += VolExtension;
320 }
321 return path;
322}
323
324FString CArchivePath::GetTempPath() const
325{
326 FString path = TempPrefix;
327 path += us2fs(Name);
328 if (!BaseExtension.IsEmpty())
329 {
330 path += '.';
331 path += us2fs(BaseExtension);
332 }
333 path += ".tmp";
334 path += TempPostfix;
335 return path;
336}
337
338static const char * const kDefaultArcType = "7z";
339static const char * const kDefaultArcExt = "7z";
340static const char * const kSFXExtension =
341 #ifdef _WIN32
342 "exe";
343 #else
344 "";
345 #endif
346
347bool CUpdateOptions::InitFormatIndex(const CCodecs *codecs,
348 const CObjectVector<COpenType> &types, const UString &arcPath)
349{
350 if (types.Size() > 1)
351 return false;
352 // int arcTypeIndex = -1;
353 if (types.Size() != 0)
354 {
355 MethodMode.Type = types[0];
356 MethodMode.Type_Defined = true;
357 }
358 if (MethodMode.Type.FormatIndex < 0)
359 {
360 // MethodMode.Type = -1;
361 MethodMode.Type = COpenType();
362 if (ArcNameMode != k_ArcNameMode_Add)
363 {
364 MethodMode.Type.FormatIndex = codecs->FindFormatForArchiveName(arcPath);
365 if (MethodMode.Type.FormatIndex >= 0)
366 MethodMode.Type_Defined = true;
367 }
368 }
369 return true;
370}
371
372bool CUpdateOptions::SetArcPath(const CCodecs *codecs, const UString &arcPath)
373{
374 UString typeExt;
375 int formatIndex = MethodMode.Type.FormatIndex;
376 if (formatIndex < 0)
377 {
378 typeExt = kDefaultArcExt;
379 }
380 else
381 {
382 const CArcInfoEx &arcInfo = codecs->Formats[(unsigned)formatIndex];
383 if (!arcInfo.UpdateEnabled)
384 return false;
385 typeExt = arcInfo.GetMainExt();
386 }
387 UString ext = typeExt;
388 if (SfxMode)
389 ext = kSFXExtension;
390 ArchivePath.BaseExtension = ext;
391 ArchivePath.VolExtension = typeExt;
392 ArchivePath.ParseFromPath(arcPath, ArcNameMode);
393 FOR_VECTOR (i, Commands)
394 {
395 CUpdateArchiveCommand &uc = Commands[i];
396 uc.ArchivePath.BaseExtension = ext;
397 uc.ArchivePath.VolExtension = typeExt;
398 uc.ArchivePath.ParseFromPath(uc.UserArchivePath, ArcNameMode);
399 }
400 return true;
401}
402
403
404struct CUpdateProduceCallbackImp: public IUpdateProduceCallback
405{
406 const CObjectVector<CArcItem> *_arcItems;
407 CDirItemsStat *_stat;
408 IUpdateCallbackUI *_callback;
409
410 CUpdateProduceCallbackImp(
411 const CObjectVector<CArcItem> *a,
412 CDirItemsStat *stat,
413 IUpdateCallbackUI *callback):
414 _arcItems(a),
415 _stat(stat),
416 _callback(callback) {}
417
418 virtual HRESULT ShowDeleteFile(unsigned arcIndex);
419};
420
421
422HRESULT CUpdateProduceCallbackImp::ShowDeleteFile(unsigned arcIndex)
423{
424 const CArcItem &ai = (*_arcItems)[arcIndex];
425 {
426 CDirItemsStat &stat = *_stat;
427 if (ai.IsDir)
428 stat.NumDirs++;
429 else if (ai.IsAltStream)
430 {
431 stat.NumAltStreams++;
432 stat.AltStreamsSize += ai.Size;
433 }
434 else
435 {
436 stat.NumFiles++;
437 stat.FilesSize += ai.Size;
438 }
439 }
440 return _callback->ShowDeleteFile(ai.Name, ai.IsDir);
441}
442
443bool CRenamePair::Prepare()
444{
445 if (RecursedType != NRecursedType::kNonRecursed)
446 return false;
447 if (!WildcardParsing)
448 return true;
449 return !DoesNameContainWildcard(OldName);
450}
451
452extern bool g_CaseSensitive;
453
454static unsigned CompareTwoNames(const wchar_t *s1, const wchar_t *s2)
455{
456 for (unsigned i = 0;; i++)
457 {
458 wchar_t c1 = s1[i];
459 wchar_t c2 = s2[i];
460 if (c1 == 0 || c2 == 0)
461 return i;
462 if (c1 == c2)
463 continue;
464 if (!g_CaseSensitive && (MyCharUpper(c1) == MyCharUpper(c2)))
465 continue;
466 if (IsPathSepar(c1) && IsPathSepar(c2))
467 continue;
468 return i;
469 }
470}
471
472bool CRenamePair::GetNewPath(bool isFolder, const UString &src, UString &dest) const
473{
474 unsigned num = CompareTwoNames(OldName, src);
475 if (OldName[num] == 0)
476 {
477 if (src[num] != 0 && !IsPathSepar(src[num]) && num != 0 && !IsPathSepar(src[num - 1]))
478 return false;
479 }
480 else
481 {
482 // OldName[num] != 0
483 // OldName = "1\1a.txt"
484 // src = "1"
485
486 if (!isFolder
487 || src[num] != 0
488 || !IsPathSepar(OldName[num])
489 || OldName[num + 1] != 0)
490 return false;
491 }
492 dest = NewName + src.Ptr(num);
493 return true;
494}
495
496#ifdef SUPPORT_ALT_STREAMS
497int FindAltStreamColon_in_Path(const wchar_t *path);
498#endif
499
500static HRESULT Compress(
501 const CUpdateOptions &options,
502 bool isUpdatingItself,
503 CCodecs *codecs,
504 const CActionSet &actionSet,
505 const CArc *arc,
506 CArchivePath &archivePath,
507 const CObjectVector<CArcItem> &arcItems,
508 Byte *processedItemsStatuses,
509 const CDirItems &dirItems,
510 const CDirItem *parentDirItem,
511 CTempFiles &tempFiles,
512 CUpdateErrorInfo &errorInfo,
513 IUpdateCallbackUI *callback,
514 CFinishArchiveStat &st)
515{
516 CMyComPtr<IOutArchive> outArchive;
517 int formatIndex = options.MethodMode.Type.FormatIndex;
518
519 if (arc)
520 {
521 formatIndex = arc->FormatIndex;
522 if (formatIndex < 0)
523 return E_NOTIMPL;
524 CMyComPtr<IInArchive> archive2 = arc->Archive;
525 HRESULT result = archive2.QueryInterface(IID_IOutArchive, &outArchive);
526 if (result != S_OK)
527 throw kUpdateIsNotSupoorted;
528 }
529 else
530 {
531 RINOK(codecs->CreateOutArchive((unsigned)formatIndex, outArchive));
532
533 #ifdef EXTERNAL_CODECS
534 {
535 CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
536 outArchive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
537 if (setCompressCodecsInfo)
538 {
539 RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs));
540 }
541 }
542 #endif
543 }
544
545 if (!outArchive)
546 throw kUpdateIsNotSupoorted;
547
548 NFileTimeType::EEnum fileTimeType;
549 {
550 UInt32 value;
551 RINOK(outArchive->GetFileTimeType(&value));
552
553 switch (value)
554 {
555 case NFileTimeType::kWindows:
556 case NFileTimeType::kUnix:
557 case NFileTimeType::kDOS:
558 fileTimeType = (NFileTimeType::EEnum)value;
559 break;
560 default:
561 return E_FAIL;
562 }
563 }
564
565 {
566 const CArcInfoEx &arcInfo = codecs->Formats[(unsigned)formatIndex];
567 if (options.AltStreams.Val && !arcInfo.Flags_AltStreams())
568 return E_NOTIMPL;
569 if (options.NtSecurity.Val && !arcInfo.Flags_NtSecure())
570 return E_NOTIMPL;
571 if (options.DeleteAfterCompressing && arcInfo.Flags_HashHandler())
572 return E_NOTIMPL;
573 }
574
575 CRecordVector<CUpdatePair2> updatePairs2;
576
577 UStringVector newNames;
578
579 CArcToDoStat stat2;
580
581 if (options.RenamePairs.Size() != 0)
582 {
583 FOR_VECTOR (i, arcItems)
584 {
585 const CArcItem &ai = arcItems[i];
586 bool needRename = false;
587 UString dest;
588
589 if (ai.Censored)
590 {
591 FOR_VECTOR (j, options.RenamePairs)
592 {
593 const CRenamePair &rp = options.RenamePairs[j];
594 if (rp.GetNewPath(ai.IsDir, ai.Name, dest))
595 {
596 needRename = true;
597 break;
598 }
599
600 #ifdef SUPPORT_ALT_STREAMS
601 if (ai.IsAltStream)
602 {
603 int colonPos = FindAltStreamColon_in_Path(ai.Name);
604 if (colonPos >= 0)
605 {
606 UString mainName = ai.Name.Left((unsigned)colonPos);
607 /*
608 actually we must improve that code to support cases
609 with folder renaming like: rn arc dir1\ dir2\
610 */
611 if (rp.GetNewPath(false, mainName, dest))
612 {
613 needRename = true;
614 dest += ':';
615 dest += ai.Name.Ptr((unsigned)(colonPos + 1));
616 break;
617 }
618 }
619 }
620 #endif
621 }
622 }
623
624 CUpdatePair2 up2;
625 up2.SetAs_NoChangeArcItem(ai.IndexInServer);
626 if (needRename)
627 {
628 up2.NewProps = true;
629 RINOK(arc->IsItemAnti(i, up2.IsAnti));
630 up2.NewNameIndex = (int)newNames.Add(dest);
631 }
632 updatePairs2.Add(up2);
633 }
634 }
635 else
636 {
637 CRecordVector<CUpdatePair> updatePairs;
638 GetUpdatePairInfoList(dirItems, arcItems, fileTimeType, updatePairs); // must be done only once!!!
639 CUpdateProduceCallbackImp upCallback(&arcItems, &stat2.DeleteData, callback);
640
641 UpdateProduce(updatePairs, actionSet, updatePairs2, isUpdatingItself ? &upCallback : NULL);
642 }
643
644 {
645 FOR_VECTOR (i, updatePairs2)
646 {
647 const CUpdatePair2 &up = updatePairs2[i];
648
649 // 17.01: anti-item is (up.NewData && (p.UseArcProps in most cases))
650
651 if (up.NewData && !up.UseArcProps)
652 {
653 if (up.ExistOnDisk())
654 {
655 CDirItemsStat2 &stat = stat2.NewData;
656 const CDirItem &di = dirItems.Items[(unsigned)up.DirIndex];
657 if (di.IsDir())
658 {
659 if (up.IsAnti)
660 stat.Anti_NumDirs++;
661 else
662 stat.NumDirs++;
663 }
664 else if (di.IsAltStream)
665 {
666 if (up.IsAnti)
667 stat.Anti_NumAltStreams++;
668 else
669 {
670 stat.NumAltStreams++;
671 stat.AltStreamsSize += di.Size;
672 }
673 }
674 else
675 {
676 if (up.IsAnti)
677 stat.Anti_NumFiles++;
678 else
679 {
680 stat.NumFiles++;
681 stat.FilesSize += di.Size;
682 }
683 }
684 }
685 }
686 else if (up.ArcIndex >= 0)
687 {
688 CDirItemsStat2 &stat = *(up.NewData ? &stat2.NewData : &stat2.OldData);
689 const CArcItem &ai = arcItems[(unsigned)up.ArcIndex];
690 if (ai.IsDir)
691 {
692 if (up.IsAnti)
693 stat.Anti_NumDirs++;
694 else
695 stat.NumDirs++;
696 }
697 else if (ai.IsAltStream)
698 {
699 if (up.IsAnti)
700 stat.Anti_NumAltStreams++;
701 else
702 {
703 stat.NumAltStreams++;
704 stat.AltStreamsSize += ai.Size;
705 }
706 }
707 else
708 {
709 if (up.IsAnti)
710 stat.Anti_NumFiles++;
711 else
712 {
713 stat.NumFiles++;
714 stat.FilesSize += ai.Size;
715 }
716 }
717 }
718 }
719 RINOK(callback->SetNumItems(stat2));
720 }
721
722 CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback;
723 CMyComPtr<IArchiveUpdateCallback> updateCallback(updateCallbackSpec);
724
725 updateCallbackSpec->PreserveATime = options.PreserveATime;
726 updateCallbackSpec->ShareForWrite = options.OpenShareForWrite;
727 updateCallbackSpec->StopAfterOpenError = options.StopAfterOpenError;
728 updateCallbackSpec->StdInMode = options.StdInMode;
729 updateCallbackSpec->Callback = callback;
730
731 if (arc)
732 {
733 // we set Archive to allow to transfer GetProperty requests back to DLL.
734 updateCallbackSpec->Archive = arc->Archive;
735 }
736
737 updateCallbackSpec->DirItems = &dirItems;
738 updateCallbackSpec->ParentDirItem = parentDirItem;
739
740 updateCallbackSpec->StoreNtSecurity = options.NtSecurity.Val;
741 updateCallbackSpec->StoreHardLinks = options.HardLinks.Val;
742 updateCallbackSpec->StoreSymLinks = options.SymLinks.Val;
743
744 updateCallbackSpec->Arc = arc;
745 updateCallbackSpec->ArcItems = &arcItems;
746 updateCallbackSpec->UpdatePairs = &updatePairs2;
747
748 updateCallbackSpec->ProcessedItemsStatuses = processedItemsStatuses;
749
750 {
751 const UString arcPath = archivePath.GetFinalPath();
752 updateCallbackSpec->ArcFileName = ExtractFileNameFromPath(arcPath);
753 }
754
755 if (options.RenamePairs.Size() != 0)
756 updateCallbackSpec->NewNames = &newNames;
757
758 CMyComPtr<IOutStream> outSeekStream;
759 CMyComPtr<ISequentialOutStream> outStream;
760
761 if (!options.StdOutMode)
762 {
763 FString dirPrefix;
764 if (!GetOnlyDirPrefix(us2fs(archivePath.GetFinalPath()), dirPrefix))
765 throw 1417161;
766 CreateComplexDir(dirPrefix);
767 }
768
769 COutFileStream *outStreamSpec = NULL;
770 CStdOutFileStream *stdOutFileStreamSpec = NULL;
771 COutMultiVolStream *volStreamSpec = NULL;
772
773 if (options.VolumesSizes.Size() == 0)
774 {
775 if (options.StdOutMode)
776 {
777 stdOutFileStreamSpec = new CStdOutFileStream;
778 outStream = stdOutFileStreamSpec;
779 }
780 else
781 {
782 outStreamSpec = new COutFileStream;
783 outSeekStream = outStreamSpec;
784 outStream = outSeekStream;
785 bool isOK = false;
786 FString realPath;
787
788 for (unsigned i = 0; i < (1 << 16); i++)
789 {
790 if (archivePath.Temp)
791 {
792 if (i > 0)
793 {
794 archivePath.TempPostfix.Empty();
795 archivePath.TempPostfix.Add_UInt32(i);
796 }
797 realPath = archivePath.GetTempPath();
798 }
799 else
800 realPath = us2fs(archivePath.GetFinalPath());
801 if (outStreamSpec->Create(realPath, false))
802 {
803 tempFiles.Paths.Add(realPath);
804 isOK = true;
805 break;
806 }
807 if (::GetLastError() != ERROR_FILE_EXISTS)
808 break;
809 if (!archivePath.Temp)
810 break;
811 }
812
813 if (!isOK)
814 return errorInfo.SetFromLastError("cannot open file", realPath);
815 }
816 }
817 else
818 {
819 if (options.StdOutMode)
820 return E_FAIL;
821 if (arc && arc->GetGlobalOffset() > 0)
822 return E_NOTIMPL;
823
824 volStreamSpec = new COutMultiVolStream;
825 outSeekStream = volStreamSpec;
826 outStream = outSeekStream;
827 volStreamSpec->Sizes = options.VolumesSizes;
828 volStreamSpec->Prefix = us2fs(archivePath.GetFinalVolPath());
829 volStreamSpec->Prefix += '.';
830 volStreamSpec->TempFiles = &tempFiles;
831 volStreamSpec->Init();
832
833 /*
834 updateCallbackSpec->VolumesSizes = volumesSizes;
835 updateCallbackSpec->VolName = archivePath.Prefix + archivePath.Name;
836 if (!archivePath.VolExtension.IsEmpty())
837 updateCallbackSpec->VolExt = UString('.') + archivePath.VolExtension;
838 */
839 }
840
841 RINOK(SetProperties(outArchive, options.MethodMode.Properties));
842
843 if (options.SfxMode)
844 {
845 CInFileStream *sfxStreamSpec = new CInFileStream;
846 CMyComPtr<IInStream> sfxStream(sfxStreamSpec);
847 if (!sfxStreamSpec->Open(options.SfxModule))
848 return errorInfo.SetFromLastError("cannot open SFX module", options.SfxModule);
849
850 CMyComPtr<ISequentialOutStream> sfxOutStream;
851 COutFileStream *outStreamSpec2 = NULL;
852 if (options.VolumesSizes.Size() == 0)
853 sfxOutStream = outStream;
854 else
855 {
856 outStreamSpec2 = new COutFileStream;
857 sfxOutStream = outStreamSpec2;
858 FString realPath = us2fs(archivePath.GetFinalPath());
859 if (!outStreamSpec2->Create(realPath, false))
860 return errorInfo.SetFromLastError("cannot open file", realPath);
861 }
862
863 {
864 UInt64 sfxSize;
865 RINOK(sfxStreamSpec->GetSize(&sfxSize));
866 RINOK(callback->WriteSfx(fs2us(options.SfxModule), sfxSize));
867 }
868
869 RINOK(NCompress::CopyStream(sfxStream, sfxOutStream, NULL));
870
871 if (outStreamSpec2)
872 {
873 RINOK(outStreamSpec2->Close());
874 }
875 }
876
877 CMyComPtr<ISequentialOutStream> tailStream;
878
879 if (options.SfxMode || !arc || arc->ArcStreamOffset == 0)
880 tailStream = outStream;
881 else
882 {
883 // Int64 globalOffset = arc->GetGlobalOffset();
884 RINOK(arc->InStream->Seek(0, STREAM_SEEK_SET, NULL));
885 RINOK(NCompress::CopyStream_ExactSize(arc->InStream, outStream, arc->ArcStreamOffset, NULL));
886 if (options.StdOutMode)
887 tailStream = outStream;
888 else
889 {
890 CTailOutStream *tailStreamSpec = new CTailOutStream;
891 tailStream = tailStreamSpec;
892 tailStreamSpec->Stream = outSeekStream;
893 tailStreamSpec->Offset = arc->ArcStreamOffset;
894 tailStreamSpec->Init();
895 }
896 }
897
898
899 HRESULT result = outArchive->UpdateItems(tailStream, updatePairs2.Size(), updateCallback);
900 // callback->Finalize();
901 RINOK(result);
902
903 if (!updateCallbackSpec->AreAllFilesClosed())
904 {
905 errorInfo.Message = "There are unclosed input file:";
906 errorInfo.FileNames = updateCallbackSpec->_openFiles_Paths;
907 return E_FAIL;
908 }
909
910 if (options.SetArcMTime)
911 {
912 FILETIME ft;
913 ft.dwLowDateTime = 0;
914 ft.dwHighDateTime = 0;
915 FOR_VECTOR (i, updatePairs2)
916 {
917 const CUpdatePair2 &pair2 = updatePairs2[i];
918 const FILETIME *ft2 = NULL;
919 if (pair2.NewProps && pair2.DirIndex >= 0)
920 ft2 = &dirItems.Items[(unsigned)pair2.DirIndex].MTime;
921 else if (pair2.UseArcProps && pair2.ArcIndex >= 0)
922 ft2 = &arcItems[(unsigned)pair2.ArcIndex].MTime;
923 if (ft2)
924 {
925 if (::CompareFileTime(&ft, ft2) < 0)
926 ft = *ft2;
927 }
928 }
929 if (ft.dwLowDateTime != 0 || ft.dwHighDateTime != 0)
930 {
931 if (outStreamSpec)
932 outStreamSpec->SetMTime(&ft);
933 else if (volStreamSpec)
934 volStreamSpec->SetMTime(&ft);
935 }
936 }
937
938 if (callback)
939 {
940 UInt64 size = 0;
941 if (outStreamSpec)
942 outStreamSpec->GetSize(&size);
943 else if (stdOutFileStreamSpec)
944 size = stdOutFileStreamSpec->GetSize();
945 else
946 size = volStreamSpec->GetSize();
947
948 st.OutArcFileSize = size;
949 }
950
951 if (outStreamSpec)
952 result = outStreamSpec->Close();
953 else if (volStreamSpec)
954 result = volStreamSpec->Close();
955
956 RINOK(result)
957
958 if (processedItemsStatuses)
959 {
960 FOR_VECTOR (i, updatePairs2)
961 {
962 const CUpdatePair2 &up = updatePairs2[i];
963 if (up.NewData && up.DirIndex >= 0)
964 {
965 const CDirItem &di = dirItems.Items[(unsigned)up.DirIndex];
966 if (di.AreReparseData() || (!di.IsDir() && di.Size == 0))
967 processedItemsStatuses[(unsigned)up.DirIndex] = 1;
968 }
969 }
970 }
971
972 return result;
973}
974
975
976
977static bool Censor_AreAllAllowed(const NWildcard::CCensor &censor)
978{
979 if (censor.Pairs.Size() != 1)
980 return false;
981 const NWildcard::CPair &pair = censor.Pairs[0];
982 /* Censor_CheckPath() ignores (CPair::Prefix).
983 So we also ignore (CPair::Prefix) here */
984 // if (!pair.Prefix.IsEmpty()) return false;
985 return pair.Head.AreAllAllowed();
986}
987
988bool CensorNode_CheckPath2(const NWildcard::CCensorNode &node, const CReadArcItem &item, bool &include);
989
990static bool Censor_CheckPath(const NWildcard::CCensor &censor, const CReadArcItem &item)
991{
992 bool finded = false;
993 FOR_VECTOR (i, censor.Pairs)
994 {
995 /* (CPair::Prefix) in not used for matching items in archive.
996 So we ignore (CPair::Prefix) here */
997 bool include;
998 if (CensorNode_CheckPath2(censor.Pairs[i].Head, item, include))
999 {
1000 // Check it and FIXME !!!!
1001 // here we can exclude item via some Pair, that is still allowed by another Pair
1002 if (!include)
1003 return false;
1004 finded = true;
1005 }
1006 }
1007 return finded;
1008}
1009
1010static HRESULT EnumerateInArchiveItems(
1011 // bool storeStreamsMode,
1012 const NWildcard::CCensor &censor,
1013 const CArc &arc,
1014 CObjectVector<CArcItem> &arcItems)
1015{
1016 arcItems.Clear();
1017 UInt32 numItems;
1018 IInArchive *archive = arc.Archive;
1019 RINOK(archive->GetNumberOfItems(&numItems));
1020 arcItems.ClearAndReserve(numItems);
1021
1022 CReadArcItem item;
1023
1024 const bool allFilesAreAllowed = Censor_AreAllAllowed(censor);
1025
1026 for (UInt32 i = 0; i < numItems; i++)
1027 {
1028 CArcItem ai;
1029
1030 RINOK(arc.GetItem(i, item));
1031 ai.Name = item.Path;
1032 ai.IsDir = item.IsDir;
1033 ai.IsAltStream =
1034 #ifdef SUPPORT_ALT_STREAMS
1035 item.IsAltStream;
1036 #else
1037 false;
1038 #endif
1039
1040 /*
1041 if (!storeStreamsMode && ai.IsAltStream)
1042 continue;
1043 */
1044 if (allFilesAreAllowed)
1045 ai.Censored = true;
1046 else
1047 ai.Censored = Censor_CheckPath(censor, item);
1048
1049 RINOK(arc.GetItemMTime(i, ai.MTime, ai.MTimeDefined));
1050 RINOK(arc.GetItemSize(i, ai.Size, ai.SizeDefined));
1051
1052 {
1053 CPropVariant prop;
1054 RINOK(archive->GetProperty(i, kpidTimeType, &prop));
1055 if (prop.vt == VT_UI4)
1056 {
1057 ai.TimeType = (int)(NFileTimeType::EEnum)prop.ulVal;
1058 switch (ai.TimeType)
1059 {
1060 case NFileTimeType::kWindows:
1061 case NFileTimeType::kUnix:
1062 case NFileTimeType::kDOS:
1063 break;
1064 default:
1065 return E_FAIL;
1066 }
1067 }
1068 }
1069
1070 ai.IndexInServer = i;
1071 arcItems.AddInReserved(ai);
1072 }
1073 return S_OK;
1074}
1075
1076#if defined(_WIN32) && !defined(UNDER_CE)
1077
1078#include <MAPI.h>
1079
1080#endif
1081
1082HRESULT UpdateArchive(
1083 CCodecs *codecs,
1084 const CObjectVector<COpenType> &types,
1085 const UString &cmdArcPath2,
1086 NWildcard::CCensor &censor,
1087 CUpdateOptions &options,
1088 CUpdateErrorInfo &errorInfo,
1089 IOpenCallbackUI *openCallback,
1090 IUpdateCallbackUI2 *callback,
1091 bool needSetPath)
1092{
1093 if (options.StdOutMode && options.EMailMode)
1094 return E_FAIL;
1095
1096 if (types.Size() > 1)
1097 return E_NOTIMPL;
1098
1099 bool renameMode = !options.RenamePairs.IsEmpty();
1100 if (renameMode)
1101 {
1102 if (options.Commands.Size() != 1)
1103 return E_FAIL;
1104 }
1105
1106 if (options.DeleteAfterCompressing)
1107 {
1108 if (options.Commands.Size() != 1)
1109 return E_NOTIMPL;
1110 const CActionSet &as = options.Commands[0].ActionSet;
1111 for (unsigned i = 2; i < NPairState::kNumValues; i++)
1112 if (as.StateActions[i] != NPairAction::kCompress)
1113 return E_NOTIMPL;
1114 }
1115
1116 censor.AddPathsToCensor(options.PathMode);
1117 #ifdef _WIN32
1118 ConvertToLongNames(censor);
1119 #endif
1120 censor.ExtendExclude();
1121
1122
1123 if (options.VolumesSizes.Size() > 0 && (options.EMailMode /* || options.SfxMode */))
1124 return E_NOTIMPL;
1125
1126 if (options.SfxMode)
1127 {
1128 CProperty property;
1129 property.Name = "rsfx";
1130 options.MethodMode.Properties.Add(property);
1131 if (options.SfxModule.IsEmpty())
1132 {
1133 errorInfo.Message = "SFX file is not specified";
1134 return E_FAIL;
1135 }
1136 bool found = false;
1137 if (options.SfxModule.Find(FCHAR_PATH_SEPARATOR) < 0)
1138 {
1139 const FString fullName = NDLL::GetModuleDirPrefix() + options.SfxModule;
1140 if (NFind::DoesFileExist_FollowLink(fullName))
1141 {
1142 options.SfxModule = fullName;
1143 found = true;
1144 }
1145 }
1146 if (!found)
1147 {
1148 if (!NFind::DoesFileExist_FollowLink(options.SfxModule))
1149 return errorInfo.SetFromLastError("cannot find specified SFX module", options.SfxModule);
1150 }
1151 }
1152
1153 CArchiveLink arcLink;
1154
1155
1156 if (needSetPath)
1157 {
1158 if (!options.InitFormatIndex(codecs, types, cmdArcPath2) ||
1159 !options.SetArcPath(codecs, cmdArcPath2))
1160 return E_NOTIMPL;
1161 }
1162
1163 UString arcPath = options.ArchivePath.GetFinalPath();
1164
1165 if (!options.VolumesSizes.IsEmpty())
1166 {
1167 arcPath = options.ArchivePath.GetFinalVolPath();
1168 arcPath += '.';
1169 arcPath += "001";
1170 }
1171
1172 if (cmdArcPath2.IsEmpty())
1173 {
1174 if (options.MethodMode.Type.FormatIndex < 0)
1175 throw "type of archive is not specified";
1176 }
1177 else
1178 {
1179 NFind::CFileInfo fi;
1180 if (!fi.Find_FollowLink(us2fs(arcPath)))
1181 {
1182 if (renameMode)
1183 throw "can't find archive";;
1184 if (options.MethodMode.Type.FormatIndex < 0)
1185 {
1186 if (!options.SetArcPath(codecs, cmdArcPath2))
1187 return E_NOTIMPL;
1188 }
1189 }
1190 else
1191 {
1192 if (fi.IsDir())
1193 return errorInfo.SetFromError_DWORD("There is a folder with the name of archive",
1194 us2fs(arcPath),
1195 #ifdef _WIN32
1196 ERROR_ACCESS_DENIED
1197 #else
1198 EISDIR
1199 #endif
1200 );
1201 if (fi.IsDevice)
1202 return E_NOTIMPL;
1203
1204 if (!options.StdOutMode && options.UpdateArchiveItself)
1205 if (fi.IsReadOnly())
1206 {
1207 return errorInfo.SetFromError_DWORD("The file is read-only",
1208 us2fs(arcPath),
1209 #ifdef _WIN32
1210 ERROR_ACCESS_DENIED
1211 #else
1212 EACCES
1213 #endif
1214 );
1215 }
1216
1217 if (options.VolumesSizes.Size() > 0)
1218 {
1219 errorInfo.FileNames.Add(us2fs(arcPath));
1220 // errorInfo.SystemError = (DWORD)E_NOTIMPL;
1221 errorInfo.Message = kUpdateIsNotSupported_MultiVol;
1222 return E_NOTIMPL;
1223 }
1224 CObjectVector<COpenType> types2;
1225 // change it.
1226 if (options.MethodMode.Type_Defined)
1227 types2.Add(options.MethodMode.Type);
1228 // We need to set Properties to open archive only in some cases (WIM archives).
1229
1230 CIntVector excl;
1231 COpenOptions op;
1232 #ifndef _SFX
1233 op.props = &options.MethodMode.Properties;
1234 #endif
1235 op.codecs = codecs;
1236 op.types = &types2;
1237 op.excludedFormats = &excl;
1238 op.stdInMode = false;
1239 op.stream = NULL;
1240 op.filePath = arcPath;
1241
1242 RINOK(callback->StartOpenArchive(arcPath));
1243
1244 HRESULT result = arcLink.Open_Strict(op, openCallback);
1245
1246 if (result == E_ABORT)
1247 return result;
1248
1249 HRESULT res2 = callback->OpenResult(codecs, arcLink, arcPath, result);
1250 /*
1251 if (result == S_FALSE)
1252 return E_FAIL;
1253 */
1254 RINOK(res2);
1255 RINOK(result);
1256
1257 if (arcLink.VolumePaths.Size() > 1)
1258 {
1259 // errorInfo.SystemError = (DWORD)E_NOTIMPL;
1260 errorInfo.Message = kUpdateIsNotSupported_MultiVol;
1261 return E_NOTIMPL;
1262 }
1263
1264 CArc &arc = arcLink.Arcs.Back();
1265 arc.MTimeDefined = !fi.IsDevice;
1266 arc.MTime = fi.MTime;
1267
1268 if (arc.ErrorInfo.ThereIsTail)
1269 {
1270 // errorInfo.SystemError = (DWORD)E_NOTIMPL;
1271 errorInfo.Message = "There is some data block after the end of the archive";
1272 return E_NOTIMPL;
1273 }
1274 if (options.MethodMode.Type.FormatIndex < 0)
1275 {
1276 options.MethodMode.Type.FormatIndex = arcLink.GetArc()->FormatIndex;
1277 if (!options.SetArcPath(codecs, cmdArcPath2))
1278 return E_NOTIMPL;
1279 }
1280 }
1281 }
1282
1283 if (options.MethodMode.Type.FormatIndex < 0)
1284 {
1285 options.MethodMode.Type.FormatIndex = codecs->FindFormatForArchiveType((UString)kDefaultArcType);
1286 if (options.MethodMode.Type.FormatIndex < 0)
1287 return E_NOTIMPL;
1288 }
1289
1290 bool thereIsInArchive = arcLink.IsOpen;
1291 if (!thereIsInArchive && renameMode)
1292 return E_FAIL;
1293
1294 CDirItems dirItems;
1295 dirItems.Callback = callback;
1296
1297 CDirItem parentDirItem;
1298 CDirItem *parentDirItem_Ptr = NULL;
1299
1300 /*
1301 FStringVector requestedPaths;
1302 FStringVector *requestedPaths_Ptr = NULL;
1303 if (options.DeleteAfterCompressing)
1304 requestedPaths_Ptr = &requestedPaths;
1305 */
1306
1307 if (options.StdInMode)
1308 {
1309 CDirItem di;
1310 di.Name = options.StdInFileName;
1311 di.Size = (UInt64)(Int64)-1;
1312 di.Attrib = 0;
1313 NTime::GetCurUtcFileTime(di.MTime);
1314 di.CTime = di.ATime = di.MTime;
1315 dirItems.Items.Add(di);
1316 }
1317 else
1318 {
1319 bool needScanning = false;
1320
1321 if (!renameMode)
1322 FOR_VECTOR (i, options.Commands)
1323 if (options.Commands[i].ActionSet.NeedScanning())
1324 needScanning = true;
1325
1326 if (needScanning)
1327 {
1328 RINOK(callback->StartScanning());
1329
1330 dirItems.SymLinks = options.SymLinks.Val;
1331
1332 #if defined(_WIN32) && !defined(UNDER_CE)
1333 dirItems.ReadSecure = options.NtSecurity.Val;
1334 #endif
1335
1336 dirItems.ScanAltStreams = options.AltStreams.Val;
1337 dirItems.ExcludeDirItems = censor.ExcludeDirItems;
1338 dirItems.ExcludeFileItems = censor.ExcludeFileItems;
1339
1340 HRESULT res = EnumerateItems(censor,
1341 options.PathMode,
1342 UString(), // options.AddPathPrefix,
1343 dirItems);
1344
1345 if (res != S_OK)
1346 {
1347 if (res != E_ABORT)
1348 errorInfo.Message = "Scanning error";
1349 return res;
1350 }
1351
1352 RINOK(callback->FinishScanning(dirItems.Stat));
1353
1354 if (censor.Pairs.Size() == 1)
1355 {
1356 NFind::CFileInfo fi;
1357 FString prefix = us2fs(censor.Pairs[0].Prefix);
1358 prefix += '.';
1359 // UString prefix = censor.Pairs[0].Prefix;
1360 /*
1361 if (prefix.Back() == WCHAR_PATH_SEPARATOR)
1362 {
1363 prefix.DeleteBack();
1364 }
1365 */
1366 if (fi.Find(prefix))
1367 if (fi.IsDir())
1368 {
1369 parentDirItem.Size = fi.Size;
1370 parentDirItem.CTime = fi.CTime;
1371 parentDirItem.ATime = fi.ATime;
1372 parentDirItem.MTime = fi.MTime;
1373 parentDirItem.Attrib = fi.Attrib;
1374 parentDirItem_Ptr = &parentDirItem;
1375
1376 int secureIndex = -1;
1377 #if defined(_WIN32) && !defined(UNDER_CE)
1378 if (options.NtSecurity.Val)
1379 dirItems.AddSecurityItem(prefix, secureIndex);
1380 #endif
1381 parentDirItem.SecureIndex = secureIndex;
1382 }
1383 }
1384 }
1385 }
1386
1387 FString tempDirPrefix;
1388 bool usesTempDir = false;
1389
1390 #ifdef _WIN32
1391 CTempDir tempDirectory;
1392 if (options.EMailMode && options.EMailRemoveAfter)
1393 {
1394 tempDirectory.Create(kTempFolderPrefix);
1395 tempDirPrefix = tempDirectory.GetPath();
1396 NormalizeDirPathPrefix(tempDirPrefix);
1397 usesTempDir = true;
1398 }
1399 #endif
1400
1401 CTempFiles tempFiles;
1402
1403 bool createTempFile = false;
1404
1405 if (!options.StdOutMode && options.UpdateArchiveItself)
1406 {
1407 CArchivePath &ap = options.Commands[0].ArchivePath;
1408 ap = options.ArchivePath;
1409 // if ((archive != 0 && !usesTempDir) || !options.WorkingDir.IsEmpty())
1410 if ((thereIsInArchive || !options.WorkingDir.IsEmpty()) && !usesTempDir && options.VolumesSizes.Size() == 0)
1411 {
1412 createTempFile = true;
1413 ap.Temp = true;
1414 if (!options.WorkingDir.IsEmpty())
1415 ap.TempPrefix = options.WorkingDir;
1416 else
1417 ap.TempPrefix = us2fs(ap.Prefix);
1418 NormalizeDirPathPrefix(ap.TempPrefix);
1419 }
1420 }
1421
1422 unsigned ci;
1423
1424
1425 // self including protection
1426 if (options.DeleteAfterCompressing)
1427 {
1428 for (ci = 0; ci < options.Commands.Size(); ci++)
1429 {
1430 CArchivePath &ap = options.Commands[ci].ArchivePath;
1431 const FString path = us2fs(ap.GetFinalPath());
1432 // maybe we must compare absolute paths path here
1433 FOR_VECTOR (i, dirItems.Items)
1434 {
1435 const FString phyPath = dirItems.GetPhyPath(i);
1436 if (phyPath == path)
1437 {
1438 UString s;
1439 s = "It is not allowed to include archive to itself";
1440 s.Add_LF();
1441 s += fs2us(path);
1442 throw s;
1443 }
1444 }
1445 }
1446 }
1447
1448
1449 for (ci = 0; ci < options.Commands.Size(); ci++)
1450 {
1451 CArchivePath &ap = options.Commands[ci].ArchivePath;
1452 if (usesTempDir)
1453 {
1454 // Check it
1455 ap.Prefix = fs2us(tempDirPrefix);
1456 // ap.Temp = true;
1457 // ap.TempPrefix = tempDirPrefix;
1458 }
1459 if (!options.StdOutMode &&
1460 (ci > 0 || !createTempFile))
1461 {
1462 const FString path = us2fs(ap.GetFinalPath());
1463 if (NFind::DoesFileOrDirExist(path))
1464 {
1465 errorInfo.SystemError = ERROR_FILE_EXISTS;
1466 errorInfo.Message = "The file already exists";
1467 errorInfo.FileNames.Add(path);
1468 return errorInfo.Get_HRESULT_Error();
1469 }
1470 }
1471 }
1472
1473 CObjectVector<CArcItem> arcItems;
1474 if (thereIsInArchive)
1475 {
1476 RINOK(EnumerateInArchiveItems(
1477 // options.StoreAltStreams,
1478 censor, arcLink.Arcs.Back(), arcItems));
1479 }
1480
1481 /*
1482 FStringVector processedFilePaths;
1483 FStringVector *processedFilePaths_Ptr = NULL;
1484 if (options.DeleteAfterCompressing)
1485 processedFilePaths_Ptr = &processedFilePaths;
1486 */
1487
1488 CByteBuffer processedItems;
1489 if (options.DeleteAfterCompressing)
1490 {
1491 const unsigned num = dirItems.Items.Size();
1492 processedItems.Alloc(num);
1493 for (unsigned i = 0; i < num; i++)
1494 processedItems[i] = 0;
1495 }
1496
1497 /*
1498 #ifndef _NO_CRYPTO
1499 if (arcLink.PasswordWasAsked)
1500 {
1501 // We set password, if open have requested password
1502 RINOK(callback->SetPassword(arcLink.Password));
1503 }
1504 #endif
1505 */
1506
1507 for (ci = 0; ci < options.Commands.Size(); ci++)
1508 {
1509 const CArc *arc = thereIsInArchive ? arcLink.GetArc() : NULL;
1510 CUpdateArchiveCommand &command = options.Commands[ci];
1511 UString name;
1512 bool isUpdating;
1513
1514 if (options.StdOutMode)
1515 {
1516 name = "stdout";
1517 isUpdating = thereIsInArchive;
1518 }
1519 else
1520 {
1521 name = command.ArchivePath.GetFinalPath();
1522 isUpdating = (ci == 0 && options.UpdateArchiveItself && thereIsInArchive);
1523 }
1524
1525 RINOK(callback->StartArchive(name, isUpdating))
1526
1527 CFinishArchiveStat st;
1528
1529 RINOK(Compress(options,
1530 isUpdating,
1531 codecs,
1532 command.ActionSet,
1533 arc,
1534 command.ArchivePath,
1535 arcItems,
1536 options.DeleteAfterCompressing ? (Byte *)processedItems : NULL,
1537
1538 dirItems,
1539 parentDirItem_Ptr,
1540
1541 tempFiles,
1542 errorInfo, callback, st));
1543
1544 RINOK(callback->FinishArchive(st));
1545 }
1546
1547
1548 if (thereIsInArchive)
1549 {
1550 RINOK(arcLink.Close());
1551 arcLink.Release();
1552 }
1553
1554 tempFiles.Paths.Clear();
1555 if (createTempFile)
1556 {
1557 try
1558 {
1559 CArchivePath &ap = options.Commands[0].ArchivePath;
1560 const FString &tempPath = ap.GetTempPath();
1561
1562 // DWORD attrib = 0;
1563 if (thereIsInArchive)
1564 {
1565 // attrib = NFind::GetFileAttrib(us2fs(arcPath));
1566 if (!DeleteFileAlways(us2fs(arcPath)))
1567 return errorInfo.SetFromLastError("cannot delete the file", us2fs(arcPath));
1568 }
1569
1570 if (!MyMoveFile(tempPath, us2fs(arcPath)))
1571 {
1572 errorInfo.SetFromLastError("cannot move the file", tempPath);
1573 errorInfo.FileNames.Add(us2fs(arcPath));
1574 return errorInfo.Get_HRESULT_Error();
1575 }
1576
1577 /*
1578 if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_READONLY))
1579 {
1580 DWORD attrib2 = NFind::GetFileAttrib(us2fs(arcPath));
1581 if (attrib2 != INVALID_FILE_ATTRIBUTES)
1582 NDir::SetFileAttrib(us2fs(arcPath), attrib2 | FILE_ATTRIBUTE_READONLY);
1583 }
1584 */
1585 }
1586 catch(...)
1587 {
1588 throw;
1589 }
1590 }
1591
1592
1593 #if defined(_WIN32) && !defined(UNDER_CE)
1594
1595 if (options.EMailMode)
1596 {
1597 NDLL::CLibrary mapiLib;
1598 if (!mapiLib.Load(FTEXT("Mapi32.dll")))
1599 {
1600 errorInfo.SetFromLastError("cannot load Mapi32.dll");
1601 return errorInfo.Get_HRESULT_Error();
1602 }
1603
1604 /*
1605 LPMAPISENDDOCUMENTS fnSend = (LPMAPISENDDOCUMENTS)mapiLib.GetProc("MAPISendDocuments");
1606 if (fnSend == 0)
1607 {
1608 errorInfo.SetFromLastError)("7-Zip cannot find MAPISendDocuments function");
1609 return errorInfo.Get_HRESULT_Error();
1610 }
1611 */
1612
1613 LPMAPISENDMAIL sendMail = (LPMAPISENDMAIL)(void *)mapiLib.GetProc("MAPISendMail");
1614 if (sendMail == 0)
1615 {
1616 errorInfo.SetFromLastError("7-Zip cannot find MAPISendMail function");
1617 return errorInfo.Get_HRESULT_Error();;
1618 }
1619
1620 FStringVector fullPaths;
1621 unsigned i;
1622
1623 for (i = 0; i < options.Commands.Size(); i++)
1624 {
1625 CArchivePath &ap = options.Commands[i].ArchivePath;
1626 FString finalPath = us2fs(ap.GetFinalPath());
1627 FString arcPath2;
1628 if (!MyGetFullPathName(finalPath, arcPath2))
1629 return errorInfo.SetFromLastError("GetFullPathName error", finalPath);
1630 fullPaths.Add(arcPath2);
1631 }
1632
1633 CCurrentDirRestorer curDirRestorer;
1634
1635 AStringVector paths;
1636 AStringVector names;
1637
1638 for (i = 0; i < fullPaths.Size(); i++)
1639 {
1640 const UString arcPath2 = fs2us(fullPaths[i]);
1641 const UString fileName = ExtractFileNameFromPath(arcPath2);
1642 paths.Add(GetAnsiString(arcPath2));
1643 names.Add(GetAnsiString(fileName));
1644 // const AString path (GetAnsiString(arcPath2));
1645 // const AString name (GetAnsiString(fileName));
1646 // Warning!!! MAPISendDocuments function changes Current directory
1647 // fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0);
1648 }
1649
1650 CRecordVector<MapiFileDesc> files;
1651 files.ClearAndSetSize(paths.Size());
1652
1653 for (i = 0; i < paths.Size(); i++)
1654 {
1655 MapiFileDesc &f = files[i];
1656 memset(&f, 0, sizeof(f));
1657 f.nPosition = 0xFFFFFFFF;
1658 f.lpszPathName = paths[i].Ptr_non_const();
1659 f.lpszFileName = names[i].Ptr_non_const();
1660 }
1661
1662 {
1663 MapiMessage m;
1664 memset(&m, 0, sizeof(m));
1665 m.nFileCount = files.Size();
1666 m.lpFiles = &files.Front();
1667
1668 const AString addr (GetAnsiString(options.EMailAddress));
1669 MapiRecipDesc rec;
1670 if (!addr.IsEmpty())
1671 {
1672 memset(&rec, 0, sizeof(rec));
1673 rec.ulRecipClass = MAPI_TO;
1674 rec.lpszAddress = addr.Ptr_non_const();
1675 m.nRecipCount = 1;
1676 m.lpRecips = &rec;
1677 }
1678
1679 sendMail((LHANDLE)0, 0, &m, MAPI_DIALOG, 0);
1680 }
1681 }
1682
1683 #endif
1684
1685 if (options.DeleteAfterCompressing)
1686 {
1687 CRecordVector<CDirPathSortPair> pairs;
1688 FStringVector foldersNames;
1689
1690 unsigned i;
1691
1692 for (i = 0; i < dirItems.Items.Size(); i++)
1693 {
1694 const CDirItem &dirItem = dirItems.Items[i];
1695 const FString phyPath = dirItems.GetPhyPath(i);
1696 if (dirItem.IsDir())
1697 {
1698 CDirPathSortPair pair;
1699 pair.Index = i;
1700 pair.SetNumSlashes(phyPath);
1701 pairs.Add(pair);
1702 }
1703 else
1704 {
1705 // 21.04: we have set processedItems[*] before for all required items
1706 if (processedItems[i] != 0
1707 // || dirItem.Size == 0
1708 // || dirItem.AreReparseData()
1709 )
1710 {
1711 NFind::CFileInfo fileInfo;
1712 /* if (!SymLinks), we follow link here, similar to (dirItem) filling */
1713 if (fileInfo.Find(phyPath, !options.SymLinks.Val))
1714 {
1715 bool is_SameSize = false;
1716 if (options.SymLinks.Val && dirItem.AreReparseData())
1717 {
1718 /* (dirItem.Size = dirItem.ReparseData.Size()) was set before.
1719 So we don't compare sizes for that case here */
1720 is_SameSize = fileInfo.IsOsSymLink();
1721 }
1722 else
1723 is_SameSize = (fileInfo.Size == dirItem.Size);
1724
1725 if (is_SameSize
1726 && CompareFileTime(&fileInfo.MTime, &dirItem.MTime) == 0
1727 && CompareFileTime(&fileInfo.CTime, &dirItem.CTime) == 0)
1728 {
1729 RINOK(callback->DeletingAfterArchiving(phyPath, false));
1730 DeleteFileAlways(phyPath);
1731 }
1732 }
1733 }
1734 else
1735 {
1736 // file was skipped by some reason. We can throw error for debug:
1737 /*
1738 errorInfo.SystemError = 0;
1739 errorInfo.Message = "file was not processed";
1740 errorInfo.FileNames.Add(phyPath);
1741 return E_FAIL;
1742 */
1743 }
1744 }
1745 }
1746
1747 pairs.Sort2();
1748
1749 for (i = 0; i < pairs.Size(); i++)
1750 {
1751 const FString phyPath = dirItems.GetPhyPath(pairs[i].Index);
1752 if (NFind::DoesDirExist(phyPath))
1753 {
1754 RINOK(callback->DeletingAfterArchiving(phyPath, true));
1755 RemoveDir(phyPath);
1756 }
1757 }
1758
1759 RINOK(callback->FinishDeletingAfterArchiving());
1760 }
1761
1762 return S_OK;
1763}
diff --git a/CPP/7zip/UI/Common/Update.h b/CPP/7zip/UI/Common/Update.h
new file mode 100644
index 0000000..06d1387
--- /dev/null
+++ b/CPP/7zip/UI/Common/Update.h
@@ -0,0 +1,208 @@
1// Update.h
2
3#ifndef __COMMON_UPDATE_H
4#define __COMMON_UPDATE_H
5
6#include "../../../Common/Wildcard.h"
7
8#include "ArchiveOpenCallback.h"
9#include "LoadCodecs.h"
10#include "OpenArchive.h"
11#include "Property.h"
12#include "UpdateAction.h"
13#include "UpdateCallback.h"
14
15#include "DirItem.h"
16
17enum EArcNameMode
18{
19 k_ArcNameMode_Smart,
20 k_ArcNameMode_Exact,
21 k_ArcNameMode_Add
22};
23
24struct CArchivePath
25{
26 UString OriginalPath;
27
28 UString Prefix; // path(folder) prefix including slash
29 UString Name; // base name
30 UString BaseExtension; // archive type extension or "exe" extension
31 UString VolExtension; // archive type extension for volumes
32
33 bool Temp;
34 FString TempPrefix; // path(folder) for temp location
35 FString TempPostfix;
36
37 CArchivePath(): Temp(false) {};
38
39 void ParseFromPath(const UString &path, EArcNameMode mode);
40 UString GetPathWithoutExt() const { return Prefix + Name; }
41 UString GetFinalPath() const;
42 UString GetFinalVolPath() const;
43 FString GetTempPath() const;
44};
45
46struct CUpdateArchiveCommand
47{
48 UString UserArchivePath;
49 CArchivePath ArchivePath;
50 NUpdateArchive::CActionSet ActionSet;
51};
52
53struct CCompressionMethodMode
54{
55 bool Type_Defined;
56 COpenType Type;
57 CObjectVector<CProperty> Properties;
58
59 CCompressionMethodMode(): Type_Defined(false) {}
60};
61
62namespace NRecursedType { enum EEnum
63{
64 kRecursed,
65 kWildcardOnlyRecursed,
66 kNonRecursed
67};}
68
69struct CRenamePair
70{
71 UString OldName;
72 UString NewName;
73 bool WildcardParsing;
74 NRecursedType::EEnum RecursedType;
75
76 CRenamePair(): WildcardParsing(true), RecursedType(NRecursedType::kNonRecursed) {}
77
78 bool Prepare();
79 bool GetNewPath(bool isFolder, const UString &src, UString &dest) const;
80};
81
82struct CUpdateOptions
83{
84 CCompressionMethodMode MethodMode;
85
86 CObjectVector<CUpdateArchiveCommand> Commands;
87 bool UpdateArchiveItself;
88 CArchivePath ArchivePath;
89 EArcNameMode ArcNameMode;
90
91 bool SfxMode;
92 FString SfxModule;
93
94 bool PreserveATime;
95 bool OpenShareForWrite;
96 bool StopAfterOpenError;
97
98 bool StdInMode;
99 UString StdInFileName;
100 bool StdOutMode;
101
102 bool EMailMode;
103 bool EMailRemoveAfter;
104 UString EMailAddress;
105
106 FString WorkingDir;
107 NWildcard::ECensorPathMode PathMode;
108 // UString AddPathPrefix;
109
110 CBoolPair NtSecurity;
111 CBoolPair AltStreams;
112 CBoolPair HardLinks;
113 CBoolPair SymLinks;
114
115 bool DeleteAfterCompressing;
116
117 bool SetArcMTime;
118
119 CObjectVector<CRenamePair> RenamePairs;
120
121 bool InitFormatIndex(const CCodecs *codecs, const CObjectVector<COpenType> &types, const UString &arcPath);
122 bool SetArcPath(const CCodecs *codecs, const UString &arcPath);
123
124 CUpdateOptions():
125 UpdateArchiveItself(true),
126 ArcNameMode(k_ArcNameMode_Smart),
127
128 SfxMode(false),
129
130 PreserveATime(false),
131 OpenShareForWrite(false),
132 StopAfterOpenError(false),
133
134 StdInMode(false),
135 StdOutMode(false),
136
137 EMailMode(false),
138 EMailRemoveAfter(false),
139
140 PathMode(NWildcard::k_RelatPath),
141
142 DeleteAfterCompressing(false),
143 SetArcMTime(false)
144
145 {};
146
147 void SetActionCommand_Add()
148 {
149 Commands.Clear();
150 CUpdateArchiveCommand c;
151 c.ActionSet = NUpdateArchive::k_ActionSet_Add;
152 Commands.Add(c);
153 }
154
155 CRecordVector<UInt64> VolumesSizes;
156};
157
158struct CUpdateErrorInfo
159{
160 DWORD SystemError; // it's DWORD (WRes) only;
161 AString Message;
162 FStringVector FileNames;
163
164 bool ThereIsError() const { return SystemError != 0 || !Message.IsEmpty() || !FileNames.IsEmpty(); }
165 HRESULT Get_HRESULT_Error() const { return SystemError == 0 ? E_FAIL : HRESULT_FROM_WIN32(SystemError); }
166 void SetFromLastError(const char *message);
167 HRESULT SetFromLastError(const char *message, const FString &fileName);
168 HRESULT SetFromError_DWORD(const char *message, const FString &fileName, DWORD error);
169
170 CUpdateErrorInfo(): SystemError(0) {};
171};
172
173struct CFinishArchiveStat
174{
175 UInt64 OutArcFileSize;
176
177 CFinishArchiveStat(): OutArcFileSize(0) {}
178};
179
180#define INTERFACE_IUpdateCallbackUI2(x) \
181 INTERFACE_IUpdateCallbackUI(x) \
182 INTERFACE_IDirItemsCallback(x) \
183 virtual HRESULT OpenResult(const CCodecs *codecs, const CArchiveLink &arcLink, const wchar_t *name, HRESULT result) x; \
184 virtual HRESULT StartScanning() x; \
185 virtual HRESULT FinishScanning(const CDirItemsStat &st) x; \
186 virtual HRESULT StartOpenArchive(const wchar_t *name) x; \
187 virtual HRESULT StartArchive(const wchar_t *name, bool updating) x; \
188 virtual HRESULT FinishArchive(const CFinishArchiveStat &st) x; \
189 virtual HRESULT DeletingAfterArchiving(const FString &path, bool isDir) x; \
190 virtual HRESULT FinishDeletingAfterArchiving() x; \
191
192struct IUpdateCallbackUI2: public IUpdateCallbackUI, public IDirItemsCallback
193{
194 INTERFACE_IUpdateCallbackUI2(=0)
195};
196
197HRESULT UpdateArchive(
198 CCodecs *codecs,
199 const CObjectVector<COpenType> &types,
200 const UString &cmdArcPath2,
201 NWildcard::CCensor &censor,
202 CUpdateOptions &options,
203 CUpdateErrorInfo &errorInfo,
204 IOpenCallbackUI *openCallback,
205 IUpdateCallbackUI2 *callback,
206 bool needSetPath);
207
208#endif
diff --git a/CPP/7zip/UI/Common/UpdateAction.cpp b/CPP/7zip/UI/Common/UpdateAction.cpp
new file mode 100644
index 0000000..a80db72
--- /dev/null
+++ b/CPP/7zip/UI/Common/UpdateAction.cpp
@@ -0,0 +1,64 @@
1// UpdateAction.cpp
2
3#include "StdAfx.h"
4
5#include "UpdateAction.h"
6
7namespace NUpdateArchive {
8
9const CActionSet k_ActionSet_Add =
10{{
11 NPairAction::kCopy,
12 NPairAction::kCopy,
13 NPairAction::kCompress,
14 NPairAction::kCompress,
15 NPairAction::kCompress,
16 NPairAction::kCompress,
17 NPairAction::kCompress
18}};
19
20const CActionSet k_ActionSet_Update =
21{{
22 NPairAction::kCopy,
23 NPairAction::kCopy,
24 NPairAction::kCompress,
25 NPairAction::kCopy,
26 NPairAction::kCompress,
27 NPairAction::kCopy,
28 NPairAction::kCompress
29}};
30
31const CActionSet k_ActionSet_Fresh =
32{{
33 NPairAction::kCopy,
34 NPairAction::kCopy,
35 NPairAction::kIgnore,
36 NPairAction::kCopy,
37 NPairAction::kCompress,
38 NPairAction::kCopy,
39 NPairAction::kCompress
40}};
41
42const CActionSet k_ActionSet_Sync =
43{{
44 NPairAction::kCopy,
45 NPairAction::kIgnore,
46 NPairAction::kCompress,
47 NPairAction::kCopy,
48 NPairAction::kCompress,
49 NPairAction::kCopy,
50 NPairAction::kCompress,
51}};
52
53const CActionSet k_ActionSet_Delete =
54{{
55 NPairAction::kCopy,
56 NPairAction::kIgnore,
57 NPairAction::kIgnore,
58 NPairAction::kIgnore,
59 NPairAction::kIgnore,
60 NPairAction::kIgnore,
61 NPairAction::kIgnore
62}};
63
64}
diff --git a/CPP/7zip/UI/Common/UpdateAction.h b/CPP/7zip/UI/Common/UpdateAction.h
new file mode 100644
index 0000000..bc53fcd
--- /dev/null
+++ b/CPP/7zip/UI/Common/UpdateAction.h
@@ -0,0 +1,66 @@
1// UpdateAction.h
2
3#ifndef __UPDATE_ACTION_H
4#define __UPDATE_ACTION_H
5
6namespace NUpdateArchive {
7
8 namespace NPairState
9 {
10 const unsigned kNumValues = 7;
11 enum EEnum
12 {
13 kNotMasked = 0,
14 kOnlyInArchive,
15 kOnlyOnDisk,
16 kNewInArchive,
17 kOldInArchive,
18 kSameFiles,
19 kUnknowNewerFiles
20 };
21 }
22
23 namespace NPairAction
24 {
25 enum EEnum
26 {
27 kIgnore = 0,
28 kCopy,
29 kCompress,
30 kCompressAsAnti
31 };
32 }
33
34 struct CActionSet
35 {
36 NPairAction::EEnum StateActions[NPairState::kNumValues];
37
38 bool IsEqualTo(const CActionSet &a) const
39 {
40 for (unsigned i = 0; i < NPairState::kNumValues; i++)
41 if (StateActions[i] != a.StateActions[i])
42 return false;
43 return true;
44 }
45
46 bool NeedScanning() const
47 {
48 unsigned i;
49 for (i = 0; i < NPairState::kNumValues; i++)
50 if (StateActions[i] == NPairAction::kCompress)
51 return true;
52 for (i = 1; i < NPairState::kNumValues; i++)
53 if (StateActions[i] != NPairAction::kIgnore)
54 return true;
55 return false;
56 }
57 };
58
59 extern const CActionSet k_ActionSet_Add;
60 extern const CActionSet k_ActionSet_Update;
61 extern const CActionSet k_ActionSet_Fresh;
62 extern const CActionSet k_ActionSet_Sync;
63 extern const CActionSet k_ActionSet_Delete;
64}
65
66#endif
diff --git a/CPP/7zip/UI/Common/UpdateCallback.cpp b/CPP/7zip/UI/Common/UpdateCallback.cpp
new file mode 100644
index 0000000..7b705ba
--- /dev/null
+++ b/CPP/7zip/UI/Common/UpdateCallback.cpp
@@ -0,0 +1,842 @@
1// UpdateCallback.cpp
2
3#include "StdAfx.h"
4
5// #include <stdio.h>
6
7#ifndef _7ZIP_ST
8#include "../../../Windows/Synchronization.h"
9#endif
10
11#include "../../../Common/ComTry.h"
12#include "../../../Common/IntToString.h"
13#include "../../../Common/StringConvert.h"
14#include "../../../Common/Wildcard.h"
15#include "../../../Common/UTFConvert.h"
16
17#include "../../../Windows/FileDir.h"
18#include "../../../Windows/FileName.h"
19#include "../../../Windows/PropVariant.h"
20
21#include "../../Common/StreamObjects.h"
22
23#include "UpdateCallback.h"
24
25#if defined(_WIN32) && !defined(UNDER_CE)
26#define _USE_SECURITY_CODE
27#include "../../../Windows/SecurityUtils.h"
28#endif
29
30using namespace NWindows;
31using namespace NFile;
32
33#ifndef _7ZIP_ST
34static NSynchronization::CCriticalSection g_CriticalSection;
35#define MT_LOCK NSynchronization::CCriticalSectionLock lock(g_CriticalSection);
36#else
37#define MT_LOCK
38#endif
39
40
41#ifdef _USE_SECURITY_CODE
42bool InitLocalPrivileges();
43#endif
44
45CArchiveUpdateCallback::CArchiveUpdateCallback():
46 _hardIndex_From((UInt32)(Int32)-1),
47
48 Callback(NULL),
49
50 DirItems(NULL),
51 ParentDirItem(NULL),
52
53 Arc(NULL),
54 ArcItems(NULL),
55 UpdatePairs(NULL),
56 NewNames(NULL),
57 CommentIndex(-1),
58 Comment(NULL),
59
60 PreserveATime(false),
61 ShareForWrite(false),
62 StopAfterOpenError(false),
63 StdInMode(false),
64
65 KeepOriginalItemNames(false),
66 StoreNtSecurity(false),
67 StoreHardLinks(false),
68 StoreSymLinks(false),
69
70 ProcessedItemsStatuses(NULL)
71{
72 #ifdef _USE_SECURITY_CODE
73 _saclEnabled = InitLocalPrivileges();
74 #endif
75}
76
77
78STDMETHODIMP CArchiveUpdateCallback::SetTotal(UInt64 size)
79{
80 COM_TRY_BEGIN
81 return Callback->SetTotal(size);
82 COM_TRY_END
83}
84
85STDMETHODIMP CArchiveUpdateCallback::SetCompleted(const UInt64 *completeValue)
86{
87 COM_TRY_BEGIN
88 return Callback->SetCompleted(completeValue);
89 COM_TRY_END
90}
91
92STDMETHODIMP CArchiveUpdateCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)
93{
94 COM_TRY_BEGIN
95 return Callback->SetRatioInfo(inSize, outSize);
96 COM_TRY_END
97}
98
99
100/*
101static const CStatProp kProps[] =
102{
103 { NULL, kpidPath, VT_BSTR},
104 { NULL, kpidIsDir, VT_BOOL},
105 { NULL, kpidSize, VT_UI8},
106 { NULL, kpidCTime, VT_FILETIME},
107 { NULL, kpidATime, VT_FILETIME},
108 { NULL, kpidMTime, VT_FILETIME},
109 { NULL, kpidAttrib, VT_UI4},
110 { NULL, kpidIsAnti, VT_BOOL}
111};
112
113STDMETHODIMP CArchiveUpdateCallback::EnumProperties(IEnumSTATPROPSTG **)
114{
115 return CStatPropEnumerator::CreateEnumerator(kProps, ARRAY_SIZE(kProps), enumerator);
116}
117*/
118
119STDMETHODIMP CArchiveUpdateCallback::GetUpdateItemInfo(UInt32 index,
120 Int32 *newData, Int32 *newProps, UInt32 *indexInArchive)
121{
122 COM_TRY_BEGIN
123 RINOK(Callback->CheckBreak());
124 const CUpdatePair2 &up = (*UpdatePairs)[index];
125 if (newData) *newData = BoolToInt(up.NewData);
126 if (newProps) *newProps = BoolToInt(up.NewProps);
127 if (indexInArchive)
128 {
129 *indexInArchive = (UInt32)(Int32)-1;
130 if (up.ExistInArchive())
131 *indexInArchive = ArcItems ? (*ArcItems)[(unsigned)up.ArcIndex].IndexInServer : (UInt32)(Int32)up.ArcIndex;
132 }
133 return S_OK;
134 COM_TRY_END
135}
136
137STDMETHODIMP CArchiveUpdateCallback::GetRootProp(PROPID propID, PROPVARIANT *value)
138{
139 NCOM::CPropVariant prop;
140 switch (propID)
141 {
142 case kpidIsDir: prop = true; break;
143 case kpidAttrib: if (ParentDirItem) prop = ParentDirItem->Attrib; break;
144 case kpidCTime: if (ParentDirItem) prop = ParentDirItem->CTime; break;
145 case kpidATime: if (ParentDirItem) prop = ParentDirItem->ATime; break;
146 case kpidMTime: if (ParentDirItem) prop = ParentDirItem->MTime; break;
147 case kpidArcFileName: if (!ArcFileName.IsEmpty()) prop = ArcFileName; break;
148 }
149 prop.Detach(value);
150 return S_OK;
151}
152
153STDMETHODIMP CArchiveUpdateCallback::GetParent(UInt32 /* index */, UInt32 *parent, UInt32 *parentType)
154{
155 *parentType = NParentType::kDir;
156 *parent = (UInt32)(Int32)-1;
157 return S_OK;
158}
159
160STDMETHODIMP CArchiveUpdateCallback::GetNumRawProps(UInt32 *numProps)
161{
162 *numProps = 0;
163 if (StoreNtSecurity)
164 *numProps = 1;
165 return S_OK;
166}
167
168STDMETHODIMP CArchiveUpdateCallback::GetRawPropInfo(UInt32 /* index */, BSTR *name, PROPID *propID)
169{
170 *name = NULL;
171 *propID = kpidNtSecure;
172 return S_OK;
173}
174
175STDMETHODIMP CArchiveUpdateCallback::GetRootRawProp(PROPID
176 #ifdef _USE_SECURITY_CODE
177 propID
178 #endif
179 , const void **data, UInt32 *dataSize, UInt32 *propType)
180{
181 *data = 0;
182 *dataSize = 0;
183 *propType = 0;
184 if (!StoreNtSecurity)
185 return S_OK;
186 #ifdef _USE_SECURITY_CODE
187 if (propID == kpidNtSecure)
188 {
189 if (StdInMode)
190 return S_OK;
191
192 if (ParentDirItem)
193 {
194 if (ParentDirItem->SecureIndex < 0)
195 return S_OK;
196 const CByteBuffer &buf = DirItems->SecureBlocks.Bufs[(unsigned)ParentDirItem->SecureIndex];
197 *data = buf;
198 *dataSize = (UInt32)buf.Size();
199 *propType = NPropDataType::kRaw;
200 return S_OK;
201 }
202
203 if (Arc && Arc->GetRootProps)
204 return Arc->GetRootProps->GetRootRawProp(propID, data, dataSize, propType);
205 }
206 #endif
207 return S_OK;
208}
209
210// #ifdef _USE_SECURITY_CODE
211// #endif
212
213STDMETHODIMP CArchiveUpdateCallback::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType)
214{
215 *data = 0;
216 *dataSize = 0;
217 *propType = 0;
218
219 if (propID == kpidNtSecure ||
220 propID == kpidNtReparse)
221 {
222 if (StdInMode)
223 return S_OK;
224
225 const CUpdatePair2 &up = (*UpdatePairs)[index];
226 if (up.UseArcProps && up.ExistInArchive() && Arc->GetRawProps)
227 return Arc->GetRawProps->GetRawProp(
228 ArcItems ? (*ArcItems)[(unsigned)up.ArcIndex].IndexInServer : (UInt32)(Int32)up.ArcIndex,
229 propID, data, dataSize, propType);
230 {
231 /*
232 if (!up.NewData)
233 return E_FAIL;
234 */
235 if (up.IsAnti)
236 return S_OK;
237
238 #if defined(_WIN32) && !defined(UNDER_CE)
239 const CDirItem &di = DirItems->Items[(unsigned)up.DirIndex];
240 #endif
241
242 #ifdef _USE_SECURITY_CODE
243 if (propID == kpidNtSecure)
244 {
245 if (!StoreNtSecurity)
246 return S_OK;
247 if (di.SecureIndex < 0)
248 return S_OK;
249 const CByteBuffer &buf = DirItems->SecureBlocks.Bufs[(unsigned)di.SecureIndex];
250 *data = buf;
251 *dataSize = (UInt32)buf.Size();
252 *propType = NPropDataType::kRaw;
253 }
254 else
255 #endif
256 if (propID == kpidNtReparse)
257 {
258 if (!StoreSymLinks)
259 return S_OK;
260 #if defined(_WIN32) && !defined(UNDER_CE)
261 // we use ReparseData2 instead of ReparseData for WIM format
262 const CByteBuffer *buf = &di.ReparseData2;
263 if (buf->Size() == 0)
264 buf = &di.ReparseData;
265 if (buf->Size() != 0)
266 {
267 *data = *buf;
268 *dataSize = (UInt32)buf->Size();
269 *propType = NPropDataType::kRaw;
270 }
271 #endif
272 }
273
274 return S_OK;
275 }
276 }
277
278 return S_OK;
279}
280
281#if defined(_WIN32) && !defined(UNDER_CE)
282
283static UString GetRelativePath(const UString &to, const UString &from)
284{
285 UStringVector partsTo, partsFrom;
286 SplitPathToParts(to, partsTo);
287 SplitPathToParts(from, partsFrom);
288
289 unsigned i;
290 for (i = 0;; i++)
291 {
292 if (i + 1 >= partsFrom.Size() ||
293 i + 1 >= partsTo.Size())
294 break;
295 if (CompareFileNames(partsFrom[i], partsTo[i]) != 0)
296 break;
297 }
298
299 if (i == 0)
300 {
301 #ifdef _WIN32
302 if (NName::IsDrivePath(to) ||
303 NName::IsDrivePath(from))
304 return to;
305 #endif
306 }
307
308 UString s;
309 unsigned k;
310
311 for (k = i + 1; k < partsFrom.Size(); k++)
312 s += ".." STRING_PATH_SEPARATOR;
313
314 for (k = i; k < partsTo.Size(); k++)
315 {
316 if (k != i)
317 s.Add_PathSepar();
318 s += partsTo[k];
319 }
320
321 return s;
322}
323
324#endif
325
326STDMETHODIMP CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
327{
328 COM_TRY_BEGIN
329 const CUpdatePair2 &up = (*UpdatePairs)[index];
330 NCOM::CPropVariant prop;
331
332 if (up.NewData)
333 {
334 /*
335 if (propID == kpidIsHardLink)
336 {
337 prop = _isHardLink;
338 prop.Detach(value);
339 return S_OK;
340 }
341 */
342 if (propID == kpidSymLink)
343 {
344 if (index == _hardIndex_From)
345 {
346 prop.Detach(value);
347 return S_OK;
348 }
349
350 #if !defined(UNDER_CE)
351
352 if (up.DirIndex >= 0)
353 {
354 const CDirItem &di = DirItems->Items[(unsigned)up.DirIndex];
355
356 #ifdef _WIN32
357 // if (di.IsDir())
358 {
359 CReparseAttr attr;
360 if (attr.Parse(di.ReparseData, di.ReparseData.Size()))
361 {
362 UString simpleName = attr.GetPath();
363 if (!attr.IsSymLink_WSL() && attr.IsRelative_Win())
364 prop = simpleName;
365 else
366 {
367 const FString phyPath = DirItems->GetPhyPath((unsigned)up.DirIndex);
368 FString fullPath;
369 if (NDir::MyGetFullPathName(phyPath, fullPath))
370 {
371 prop = GetRelativePath(simpleName, fs2us(fullPath));
372 }
373 }
374 prop.Detach(value);
375 return S_OK;
376 }
377 }
378
379 #else // _WIN32
380
381 if (di.ReparseData.Size() != 0)
382 {
383 AString utf;
384 utf.SetFrom_CalcLen((const char *)(const Byte *)di.ReparseData, (unsigned)di.ReparseData.Size());
385
386 UString us;
387 if (ConvertUTF8ToUnicode(utf, us))
388 {
389 prop = us;
390 prop.Detach(value);
391 return S_OK;
392 }
393 }
394
395 #endif // _WIN32
396 }
397 #endif // !defined(UNDER_CE)
398 }
399 else if (propID == kpidHardLink)
400 {
401 if (index == _hardIndex_From)
402 {
403 const CKeyKeyValPair &pair = _map[_hardIndex_To];
404 const CUpdatePair2 &up2 = (*UpdatePairs)[pair.Value];
405 prop = DirItems->GetLogPath((unsigned)up2.DirIndex);
406 prop.Detach(value);
407 return S_OK;
408 }
409 if (up.DirIndex >= 0)
410 {
411 prop.Detach(value);
412 return S_OK;
413 }
414 }
415 }
416
417 if (up.IsAnti
418 && propID != kpidIsDir
419 && propID != kpidPath
420 && propID != kpidIsAltStream)
421 {
422 switch (propID)
423 {
424 case kpidSize: prop = (UInt64)0; break;
425 case kpidIsAnti: prop = true; break;
426 }
427 }
428 else if (propID == kpidPath && up.NewNameIndex >= 0)
429 prop = (*NewNames)[(unsigned)up.NewNameIndex];
430 else if (propID == kpidComment
431 && CommentIndex >= 0
432 && (unsigned)CommentIndex == index
433 && Comment)
434 prop = *Comment;
435 else if (propID == kpidShortName && up.NewNameIndex >= 0 && up.IsMainRenameItem)
436 {
437 // we can generate new ShortName here;
438 }
439 else if ((up.UseArcProps || (KeepOriginalItemNames && (propID == kpidPath || propID == kpidIsAltStream)))
440 && up.ExistInArchive() && Archive)
441 return Archive->GetProperty(ArcItems ? (*ArcItems)[(unsigned)up.ArcIndex].IndexInServer : (UInt32)(Int32)up.ArcIndex, propID, value);
442 else if (up.ExistOnDisk())
443 {
444 const CDirItem &di = DirItems->Items[(unsigned)up.DirIndex];
445 switch (propID)
446 {
447 case kpidPath: prop = DirItems->GetLogPath((unsigned)up.DirIndex); break;
448 case kpidIsDir: prop = di.IsDir(); break;
449 case kpidSize: prop = di.IsDir() ? (UInt64)0 : di.Size; break;
450 case kpidAttrib: prop = di.Attrib; break;
451 case kpidCTime: prop = di.CTime; break;
452 case kpidATime: prop = di.ATime; break;
453 case kpidMTime: prop = di.MTime; break;
454 case kpidIsAltStream: prop = di.IsAltStream; break;
455 #if defined(_WIN32) && !defined(UNDER_CE)
456 // case kpidShortName: prop = di.ShortName; break;
457 #endif
458 case kpidPosixAttrib:
459 {
460 #ifdef _WIN32
461 prop = di.GetPosixAttrib();
462 #else
463 if (di.Attrib & FILE_ATTRIBUTE_UNIX_EXTENSION)
464 prop = (UInt32)(di.Attrib >> 16);
465 #endif
466 break;
467 }
468 }
469 }
470 prop.Detach(value);
471 return S_OK;
472 COM_TRY_END
473}
474
475#ifndef _7ZIP_ST
476static NSynchronization::CCriticalSection CS;
477#endif
478
479void CArchiveUpdateCallback::UpdateProcessedItemStatus(unsigned dirIndex)
480{
481 if (ProcessedItemsStatuses)
482 {
483 #ifndef _7ZIP_ST
484 NSynchronization::CCriticalSectionLock lock(CS);
485 #endif
486 ProcessedItemsStatuses[dirIndex] = 1;
487 }
488}
489
490STDMETHODIMP CArchiveUpdateCallback::GetStream2(UInt32 index, ISequentialInStream **inStream, UInt32 mode)
491{
492 COM_TRY_BEGIN
493 *inStream = NULL;
494 const CUpdatePair2 &up = (*UpdatePairs)[index];
495 if (!up.NewData)
496 return E_FAIL;
497
498 RINOK(Callback->CheckBreak());
499 // RINOK(Callback->Finalize());
500
501 bool isDir = IsDir(up);
502
503 if (up.IsAnti)
504 {
505 UString name;
506 if (up.ArcIndex >= 0)
507 name = (*ArcItems)[(unsigned)up.ArcIndex].Name;
508 else if (up.DirIndex >= 0)
509 name = DirItems->GetLogPath((unsigned)up.DirIndex);
510 RINOK(Callback->GetStream(name, isDir, true, mode));
511
512 /* 9.33: fixed. Handlers expect real stream object for files, even for anti-file.
513 so we return empty stream */
514
515 if (!isDir)
516 {
517 CBufInStream *inStreamSpec = new CBufInStream();
518 CMyComPtr<ISequentialInStream> inStreamLoc = inStreamSpec;
519 inStreamSpec->Init(NULL, 0);
520 *inStream = inStreamLoc.Detach();
521 }
522 return S_OK;
523 }
524
525 RINOK(Callback->GetStream(DirItems->GetLogPath((unsigned)up.DirIndex), isDir, false, mode));
526
527 if (isDir)
528 return S_OK;
529
530 if (StdInMode)
531 {
532 if (mode != NUpdateNotifyOp::kAdd &&
533 mode != NUpdateNotifyOp::kUpdate)
534 return S_OK;
535
536 CStdInFileStream *inStreamSpec = new CStdInFileStream;
537 CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
538 *inStream = inStreamLoc.Detach();
539 }
540 else
541 {
542 #if !defined(UNDER_CE)
543 const CDirItem &di = DirItems->Items[(unsigned)up.DirIndex];
544 if (di.AreReparseData())
545 {
546 /*
547 // we still need DeviceIoControlOut() instead of Read
548 if (!inStreamSpec->File.OpenReparse(path))
549 {
550 return Callback->OpenFileError(path, ::GetLastError());
551 }
552 */
553 // 20.03: we use Reparse Data instead of real data
554
555 CBufInStream *inStreamSpec = new CBufInStream();
556 CMyComPtr<ISequentialInStream> inStreamLoc = inStreamSpec;
557 inStreamSpec->Init(di.ReparseData, di.ReparseData.Size());
558 *inStream = inStreamLoc.Detach();
559
560 UpdateProcessedItemStatus((unsigned)up.DirIndex);
561 return S_OK;
562 }
563 #endif // !defined(UNDER_CE)
564
565 CInFileStream *inStreamSpec = new CInFileStream;
566 CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
567
568 inStreamSpec->SupportHardLinks = StoreHardLinks;
569 inStreamSpec->File.PreserveATime = PreserveATime;
570
571 const FString path = DirItems->GetPhyPath((unsigned)up.DirIndex);
572 _openFiles_Indexes.Add(index);
573 _openFiles_Paths.Add(path);
574
575 /* 21.02 : we set Callback/CallbackRef after _openFiles_Indexes adding
576 for correct working if exception was raised in GetPhyPath */
577 inStreamSpec->Callback = this;
578 inStreamSpec->CallbackRef = index;
579
580 if (!inStreamSpec->OpenShared(path, ShareForWrite))
581 {
582 DWORD error = ::GetLastError();
583 HRESULT hres = Callback->OpenFileError(path, error);
584 if (StopAfterOpenError)
585 if (hres == S_OK || hres == S_FALSE)
586 return HRESULT_FROM_WIN32(error);
587 return hres;
588 }
589
590 // #if defined(USE_WIN_FILE) || !defined(_WIN32)
591 if (StoreHardLinks)
592 {
593 CStreamFileProps props;
594 if (inStreamSpec->GetProps2(&props) == S_OK)
595 {
596 if (props.NumLinks > 1)
597 {
598 CKeyKeyValPair pair;
599 pair.Key1 = props.VolID;
600 pair.Key2 = props.FileID_Low;
601 pair.Value = index;
602 unsigned numItems = _map.Size();
603 unsigned pairIndex = _map.AddToUniqueSorted2(pair);
604 if (numItems == _map.Size())
605 {
606 // const CKeyKeyValPair &pair2 = _map.Pairs[pairIndex];
607 _hardIndex_From = index;
608 _hardIndex_To = pairIndex;
609 // we could return NULL as stream, but it's better to return real stream
610 // return S_OK;
611 }
612 }
613 }
614 }
615 // #endif
616
617 UpdateProcessedItemStatus((unsigned)up.DirIndex);
618 *inStream = inStreamLoc.Detach();
619 }
620
621 return S_OK;
622 COM_TRY_END
623}
624
625STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 opRes)
626{
627 COM_TRY_BEGIN
628 return Callback->SetOperationResult(opRes);
629 COM_TRY_END
630}
631
632STDMETHODIMP CArchiveUpdateCallback::GetStream(UInt32 index, ISequentialInStream **inStream)
633{
634 COM_TRY_BEGIN
635 return GetStream2(index, inStream,
636 (*UpdatePairs)[index].ArcIndex < 0 ?
637 NUpdateNotifyOp::kAdd :
638 NUpdateNotifyOp::kUpdate);
639 COM_TRY_END
640}
641
642STDMETHODIMP CArchiveUpdateCallback::ReportOperation(UInt32 indexType, UInt32 index, UInt32 op)
643{
644 COM_TRY_BEGIN
645
646 bool isDir = false;
647
648 if (indexType == NArchive::NEventIndexType::kOutArcIndex)
649 {
650 UString name;
651 if (index != (UInt32)(Int32)-1)
652 {
653 const CUpdatePair2 &up = (*UpdatePairs)[index];
654 if (up.ExistOnDisk())
655 {
656 name = DirItems->GetLogPath((unsigned)up.DirIndex);
657 isDir = DirItems->Items[(unsigned)up.DirIndex].IsDir();
658 }
659 }
660 return Callback->ReportUpdateOperation(op, name.IsEmpty() ? NULL : name.Ptr(), isDir);
661 }
662
663 wchar_t temp[16];
664 UString s2;
665 const wchar_t *s = NULL;
666
667 if (indexType == NArchive::NEventIndexType::kInArcIndex)
668 {
669 if (index != (UInt32)(Int32)-1)
670 {
671 if (ArcItems)
672 {
673 const CArcItem &ai = (*ArcItems)[index];
674 s = ai.Name;
675 isDir = ai.IsDir;
676 }
677 else if (Arc)
678 {
679 RINOK(Arc->GetItemPath(index, s2));
680 s = s2;
681 RINOK(Archive_IsItem_Dir(Arc->Archive, index, isDir));
682 }
683 }
684 }
685 else if (indexType == NArchive::NEventIndexType::kBlockIndex)
686 {
687 temp[0] = '#';
688 ConvertUInt32ToString(index, temp + 1);
689 s = temp;
690 }
691
692 if (!s)
693 s = L"";
694
695 return Callback->ReportUpdateOperation(op, s, isDir);
696
697 COM_TRY_END
698}
699
700STDMETHODIMP CArchiveUpdateCallback::ReportExtractResult(UInt32 indexType, UInt32 index, Int32 opRes)
701{
702 COM_TRY_BEGIN
703
704 bool isEncrypted = false;
705 wchar_t temp[16];
706 UString s2;
707 const wchar_t *s = NULL;
708
709 if (indexType == NArchive::NEventIndexType::kOutArcIndex)
710 {
711 /*
712 UString name;
713 if (index != (UInt32)(Int32)-1)
714 {
715 const CUpdatePair2 &up = (*UpdatePairs)[index];
716 if (up.ExistOnDisk())
717 {
718 s2 = DirItems->GetLogPath(up.DirIndex);
719 s = s2;
720 }
721 }
722 */
723 return E_FAIL;
724 }
725
726 if (indexType == NArchive::NEventIndexType::kInArcIndex)
727 {
728 if (index != (UInt32)(Int32)-1)
729 {
730 if (ArcItems)
731 s = (*ArcItems)[index].Name;
732 else if (Arc)
733 {
734 RINOK(Arc->GetItemPath(index, s2));
735 s = s2;
736 }
737 if (Archive)
738 {
739 RINOK(Archive_GetItemBoolProp(Archive, index, kpidEncrypted, isEncrypted));
740 }
741 }
742 }
743 else if (indexType == NArchive::NEventIndexType::kBlockIndex)
744 {
745 temp[0] = '#';
746 ConvertUInt32ToString(index, temp + 1);
747 s = temp;
748 }
749
750 return Callback->ReportExtractResult(opRes, BoolToInt(isEncrypted), s);
751
752 COM_TRY_END
753}
754
755STDMETHODIMP CArchiveUpdateCallback::GetVolumeSize(UInt32 index, UInt64 *size)
756{
757 if (VolumesSizes.Size() == 0)
758 return S_FALSE;
759 if (index >= (UInt32)VolumesSizes.Size())
760 index = VolumesSizes.Size() - 1;
761 *size = VolumesSizes[index];
762 return S_OK;
763}
764
765STDMETHODIMP CArchiveUpdateCallback::GetVolumeStream(UInt32 index, ISequentialOutStream **volumeStream)
766{
767 COM_TRY_BEGIN
768 char temp[16];
769 ConvertUInt32ToString(index + 1, temp);
770 FString res (temp);
771 while (res.Len() < 2)
772 res.InsertAtFront(FTEXT('0'));
773 FString fileName = VolName;
774 fileName += '.';
775 fileName += res;
776 fileName += VolExt;
777 COutFileStream *streamSpec = new COutFileStream;
778 CMyComPtr<ISequentialOutStream> streamLoc(streamSpec);
779 if (!streamSpec->Create(fileName, false))
780 return GetLastError_noZero_HRESULT();
781 *volumeStream = streamLoc.Detach();
782 return S_OK;
783 COM_TRY_END
784}
785
786STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password)
787{
788 COM_TRY_BEGIN
789 return Callback->CryptoGetTextPassword2(passwordIsDefined, password);
790 COM_TRY_END
791}
792
793STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword(BSTR *password)
794{
795 COM_TRY_BEGIN
796 return Callback->CryptoGetTextPassword(password);
797 COM_TRY_END
798}
799
800HRESULT CArchiveUpdateCallback::InFileStream_On_Error(UINT_PTR val, DWORD error)
801{
802 #ifdef _WIN32 // FIX IT !!!
803 // why did we check only for ERROR_LOCK_VIOLATION ?
804 // if (error == ERROR_LOCK_VIOLATION)
805 #endif
806 {
807 MT_LOCK
808 UInt32 index = (UInt32)val;
809 FOR_VECTOR(i, _openFiles_Indexes)
810 {
811 if (_openFiles_Indexes[i] == index)
812 {
813 RINOK(Callback->ReadingFileError(_openFiles_Paths[i], error));
814 break;
815 }
816 }
817 }
818 return HRESULT_FROM_WIN32(error);
819}
820
821void CArchiveUpdateCallback::InFileStream_On_Destroy(UINT_PTR val)
822{
823 {
824 MT_LOCK
825 UInt32 index = (UInt32)val;
826 FOR_VECTOR(i, _openFiles_Indexes)
827 {
828 if (_openFiles_Indexes[i] == index)
829 {
830 _openFiles_Indexes.Delete(i);
831 _openFiles_Paths.Delete(i);
832 return;
833 }
834 }
835 }
836 /* 21.02 : this function can be called in destructor.
837 And destructor can be called after some exception.
838 If we don't want to throw exception in desctructors or after another exceptions,
839 we must disable the code below that raises new exception.
840 */
841 // throw 20141125;
842}
diff --git a/CPP/7zip/UI/Common/UpdateCallback.h b/CPP/7zip/UI/Common/UpdateCallback.h
new file mode 100644
index 0000000..d27776e
--- /dev/null
+++ b/CPP/7zip/UI/Common/UpdateCallback.h
@@ -0,0 +1,165 @@
1// UpdateCallback.h
2
3#ifndef __UPDATE_CALLBACK_H
4#define __UPDATE_CALLBACK_H
5
6#include "../../../Common/MyCom.h"
7
8#include "../../Common/FileStreams.h"
9
10#include "../../IPassword.h"
11#include "../../ICoder.h"
12
13#include "../Common/UpdatePair.h"
14#include "../Common/UpdateProduce.h"
15
16#include "OpenArchive.h"
17
18struct CArcToDoStat
19{
20 CDirItemsStat2 NewData;
21 CDirItemsStat2 OldData;
22 CDirItemsStat2 DeleteData;
23
24 UInt64 Get_NumDataItems_Total() const
25 {
26 return NewData.Get_NumDataItems2() + OldData.Get_NumDataItems2();
27 }
28};
29
30#define INTERFACE_IUpdateCallbackUI(x) \
31 virtual HRESULT WriteSfx(const wchar_t *name, UInt64 size) x; \
32 virtual HRESULT SetTotal(UInt64 size) x; \
33 virtual HRESULT SetCompleted(const UInt64 *completeValue) x; \
34 virtual HRESULT SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) x; \
35 virtual HRESULT CheckBreak() x; \
36 /* virtual HRESULT Finalize() x; */ \
37 virtual HRESULT SetNumItems(const CArcToDoStat &stat) x; \
38 virtual HRESULT GetStream(const wchar_t *name, bool isDir, bool isAnti, UInt32 mode) x; \
39 virtual HRESULT OpenFileError(const FString &path, DWORD systemError) x; \
40 virtual HRESULT ReadingFileError(const FString &path, DWORD systemError) x; \
41 virtual HRESULT SetOperationResult(Int32 opRes) x; \
42 virtual HRESULT ReportExtractResult(Int32 opRes, Int32 isEncrypted, const wchar_t *name) x; \
43 virtual HRESULT ReportUpdateOperation(UInt32 op, const wchar_t *name, bool isDir) x; \
44 /* virtual HRESULT SetPassword(const UString &password) x; */ \
45 virtual HRESULT CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password) x; \
46 virtual HRESULT CryptoGetTextPassword(BSTR *password) x; \
47 virtual HRESULT ShowDeleteFile(const wchar_t *name, bool isDir) x; \
48 /* virtual HRESULT CloseProgress() { return S_OK; } */
49
50struct IUpdateCallbackUI
51{
52 INTERFACE_IUpdateCallbackUI(=0)
53};
54
55struct CKeyKeyValPair
56{
57 UInt64 Key1;
58 UInt64 Key2;
59 unsigned Value;
60
61 int Compare(const CKeyKeyValPair &a) const
62 {
63 if (Key1 < a.Key1) return -1;
64 if (Key1 > a.Key1) return 1;
65 return MyCompare(Key2, a.Key2);
66 }
67};
68
69
70class CArchiveUpdateCallback:
71 public IArchiveUpdateCallback2,
72 public IArchiveUpdateCallbackFile,
73 public IArchiveExtractCallbackMessage,
74 public IArchiveGetRawProps,
75 public IArchiveGetRootProps,
76 public ICryptoGetTextPassword2,
77 public ICryptoGetTextPassword,
78 public ICompressProgressInfo,
79 public IInFileStream_Callback,
80 public CMyUnknownImp
81{
82 #if defined(_WIN32) && !defined(UNDER_CE)
83 bool _saclEnabled;
84 #endif
85 CRecordVector<CKeyKeyValPair> _map;
86
87 UInt32 _hardIndex_From;
88 UInt32 _hardIndex_To;
89
90 void UpdateProcessedItemStatus(unsigned dirIndex);
91
92public:
93 MY_QUERYINTERFACE_BEGIN2(IArchiveUpdateCallback2)
94 MY_QUERYINTERFACE_ENTRY(IArchiveUpdateCallbackFile)
95 MY_QUERYINTERFACE_ENTRY(IArchiveExtractCallbackMessage)
96 MY_QUERYINTERFACE_ENTRY(IArchiveGetRawProps)
97 MY_QUERYINTERFACE_ENTRY(IArchiveGetRootProps)
98 MY_QUERYINTERFACE_ENTRY(ICryptoGetTextPassword2)
99 MY_QUERYINTERFACE_ENTRY(ICryptoGetTextPassword)
100 MY_QUERYINTERFACE_ENTRY(ICompressProgressInfo)
101 MY_QUERYINTERFACE_END
102 MY_ADDREF_RELEASE
103
104
105 STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize);
106
107 INTERFACE_IArchiveUpdateCallback2(;)
108 INTERFACE_IArchiveUpdateCallbackFile(;)
109 INTERFACE_IArchiveExtractCallbackMessage(;)
110 INTERFACE_IArchiveGetRawProps(;)
111 INTERFACE_IArchiveGetRootProps(;)
112
113 STDMETHOD(CryptoGetTextPassword2)(Int32 *passwordIsDefined, BSTR *password);
114 STDMETHOD(CryptoGetTextPassword)(BSTR *password);
115
116 CRecordVector<UInt32> _openFiles_Indexes;
117 FStringVector _openFiles_Paths;
118
119 bool AreAllFilesClosed() const { return _openFiles_Indexes.IsEmpty(); }
120 virtual HRESULT InFileStream_On_Error(UINT_PTR val, DWORD error);
121 virtual void InFileStream_On_Destroy(UINT_PTR val);
122
123 CRecordVector<UInt64> VolumesSizes;
124 FString VolName;
125 FString VolExt;
126 UString ArcFileName; // without path prefix
127
128 IUpdateCallbackUI *Callback;
129
130 const CDirItems *DirItems;
131 const CDirItem *ParentDirItem;
132
133 const CArc *Arc;
134 CMyComPtr<IInArchive> Archive;
135 const CObjectVector<CArcItem> *ArcItems;
136 const CRecordVector<CUpdatePair2> *UpdatePairs;
137 const UStringVector *NewNames;
138 int CommentIndex;
139 const UString *Comment;
140
141 bool PreserveATime;
142 bool ShareForWrite;
143 bool StopAfterOpenError;
144 bool StdInMode;
145
146 bool KeepOriginalItemNames;
147 bool StoreNtSecurity;
148 bool StoreHardLinks;
149 bool StoreSymLinks;
150
151 Byte *ProcessedItemsStatuses;
152
153 CArchiveUpdateCallback();
154
155 bool IsDir(const CUpdatePair2 &up) const
156 {
157 if (up.DirIndex >= 0)
158 return DirItems->Items[(unsigned)up.DirIndex].IsDir();
159 else if (up.ArcIndex >= 0)
160 return (*ArcItems)[(unsigned)up.ArcIndex].IsDir;
161 return false;
162 }
163};
164
165#endif
diff --git a/CPP/7zip/UI/Common/UpdatePair.cpp b/CPP/7zip/UI/Common/UpdatePair.cpp
new file mode 100644
index 0000000..31d73f9
--- /dev/null
+++ b/CPP/7zip/UI/Common/UpdatePair.cpp
@@ -0,0 +1,235 @@
1// UpdatePair.cpp
2
3#include "StdAfx.h"
4
5#include <time.h>
6
7#include "../../../Common/Wildcard.h"
8
9#include "../../../Windows/TimeUtils.h"
10
11#include "SortUtils.h"
12#include "UpdatePair.h"
13
14using namespace NWindows;
15using namespace NTime;
16
17static int MyCompareTime(NFileTimeType::EEnum fileTimeType, const FILETIME &time1, const FILETIME &time2)
18{
19 switch (fileTimeType)
20 {
21 case NFileTimeType::kWindows:
22 return ::CompareFileTime(&time1, &time2);
23 case NFileTimeType::kUnix:
24 {
25 UInt32 unixTime1, unixTime2;
26 FileTimeToUnixTime(time1, unixTime1);
27 FileTimeToUnixTime(time2, unixTime2);
28 return MyCompare(unixTime1, unixTime2);
29 }
30 case NFileTimeType::kDOS:
31 {
32 UInt32 dosTime1, dosTime2;
33 FileTimeToDosTime(time1, dosTime1);
34 FileTimeToDosTime(time2, dosTime2);
35 return MyCompare(dosTime1, dosTime2);
36 }
37 }
38 throw 4191618;
39}
40
41static const char * const k_Duplicate_inArc_Message = "Duplicate filename in archive:";
42static const char * const k_Duplicate_inDir_Message = "Duplicate filename on disk:";
43static const char * const k_NotCensoredCollision_Message = "Internal file name collision (file on disk, file in archive):";
44
45MY_ATTR_NORETURN
46static
47void ThrowError(const char *message, const UString &s1, const UString &s2)
48{
49 UString m (message);
50 m.Add_LF(); m += s1;
51 m.Add_LF(); m += s2;
52 throw m;
53}
54
55static int CompareArcItemsBase(const CArcItem &ai1, const CArcItem &ai2)
56{
57 int res = CompareFileNames(ai1.Name, ai2.Name);
58 if (res != 0)
59 return res;
60 if (ai1.IsDir != ai2.IsDir)
61 return ai1.IsDir ? -1 : 1;
62 return 0;
63}
64
65static int CompareArcItems(const unsigned *p1, const unsigned *p2, void *param)
66{
67 unsigned i1 = *p1;
68 unsigned i2 = *p2;
69 const CObjectVector<CArcItem> &arcItems = *(const CObjectVector<CArcItem> *)param;
70 int res = CompareArcItemsBase(arcItems[i1], arcItems[i2]);
71 if (res != 0)
72 return res;
73 return MyCompare(i1, i2);
74}
75
76void GetUpdatePairInfoList(
77 const CDirItems &dirItems,
78 const CObjectVector<CArcItem> &arcItems,
79 NFileTimeType::EEnum fileTimeType,
80 CRecordVector<CUpdatePair> &updatePairs)
81{
82 CUIntVector dirIndices, arcIndices;
83
84 unsigned numDirItems = dirItems.Items.Size();
85 unsigned numArcItems = arcItems.Size();
86
87 CIntArr duplicatedArcItem(numArcItems);
88 {
89 int *vals = &duplicatedArcItem[0];
90 for (unsigned i = 0; i < numArcItems; i++)
91 vals[i] = 0;
92 }
93
94 {
95 arcIndices.ClearAndSetSize(numArcItems);
96 if (numArcItems != 0)
97 {
98 unsigned *vals = &arcIndices[0];
99 for (unsigned i = 0; i < numArcItems; i++)
100 vals[i] = i;
101 }
102 arcIndices.Sort(CompareArcItems, (void *)&arcItems);
103 for (unsigned i = 0; i + 1 < numArcItems; i++)
104 if (CompareArcItemsBase(
105 arcItems[arcIndices[i]],
106 arcItems[arcIndices[i + 1]]) == 0)
107 {
108 duplicatedArcItem[i] = 1;
109 duplicatedArcItem[i + 1] = -1;
110 }
111 }
112
113 UStringVector dirNames;
114 {
115 dirNames.ClearAndReserve(numDirItems);
116 unsigned i;
117 for (i = 0; i < numDirItems; i++)
118 dirNames.AddInReserved(dirItems.GetLogPath(i));
119 SortFileNames(dirNames, dirIndices);
120 for (i = 0; i + 1 < numDirItems; i++)
121 {
122 const UString &s1 = dirNames[dirIndices[i]];
123 const UString &s2 = dirNames[dirIndices[i + 1]];
124 if (CompareFileNames(s1, s2) == 0)
125 ThrowError(k_Duplicate_inDir_Message, s1, s2);
126 }
127 }
128
129 unsigned dirIndex = 0;
130 unsigned arcIndex = 0;
131
132 int prevHostFile = -1;
133 const UString *prevHostName = NULL;
134
135 while (dirIndex < numDirItems || arcIndex < numArcItems)
136 {
137 CUpdatePair pair;
138
139 int dirIndex2 = -1;
140 int arcIndex2 = -1;
141 const CDirItem *di = NULL;
142 const CArcItem *ai = NULL;
143
144 int compareResult = -1;
145 const UString *name = NULL;
146
147 if (dirIndex < numDirItems)
148 {
149 dirIndex2 = (int)dirIndices[dirIndex];
150 di = &dirItems.Items[(unsigned)dirIndex2];
151 }
152
153 if (arcIndex < numArcItems)
154 {
155 arcIndex2 = (int)arcIndices[arcIndex];
156 ai = &arcItems[(unsigned)arcIndex2];
157 compareResult = 1;
158 if (dirIndex < numDirItems)
159 {
160 compareResult = CompareFileNames(dirNames[(unsigned)dirIndex2], ai->Name);
161 if (compareResult == 0)
162 {
163 if (di->IsDir() != ai->IsDir)
164 compareResult = (ai->IsDir ? 1 : -1);
165 }
166 }
167 }
168
169 if (compareResult < 0)
170 {
171 name = &dirNames[(unsigned)dirIndex2];
172 pair.State = NUpdateArchive::NPairState::kOnlyOnDisk;
173 pair.DirIndex = dirIndex2;
174 dirIndex++;
175 }
176 else if (compareResult > 0)
177 {
178 name = &ai->Name;
179 pair.State = ai->Censored ?
180 NUpdateArchive::NPairState::kOnlyInArchive:
181 NUpdateArchive::NPairState::kNotMasked;
182 pair.ArcIndex = arcIndex2;
183 arcIndex++;
184 }
185 else
186 {
187 int dupl = duplicatedArcItem[arcIndex];
188 if (dupl != 0)
189 ThrowError(k_Duplicate_inArc_Message, ai->Name, arcItems[arcIndices[(unsigned)((int)arcIndex + dupl)]].Name);
190
191 name = &dirNames[(unsigned)dirIndex2];
192 if (!ai->Censored)
193 ThrowError(k_NotCensoredCollision_Message, *name, ai->Name);
194
195 pair.DirIndex = dirIndex2;
196 pair.ArcIndex = arcIndex2;
197
198 switch (ai->MTimeDefined ? MyCompareTime(
199 ai->TimeType != - 1 ? (NFileTimeType::EEnum)ai->TimeType : fileTimeType,
200 di->MTime, ai->MTime): 0)
201 {
202 case -1: pair.State = NUpdateArchive::NPairState::kNewInArchive; break;
203 case 1: pair.State = NUpdateArchive::NPairState::kOldInArchive; break;
204 default:
205 pair.State = (ai->SizeDefined && di->Size == ai->Size) ?
206 NUpdateArchive::NPairState::kSameFiles :
207 NUpdateArchive::NPairState::kUnknowNewerFiles;
208 }
209
210 dirIndex++;
211 arcIndex++;
212 }
213
214 if ((di && di->IsAltStream) ||
215 (ai && ai->IsAltStream))
216 {
217 if (prevHostName)
218 {
219 unsigned hostLen = prevHostName->Len();
220 if (name->Len() > hostLen)
221 if ((*name)[hostLen] == ':' && CompareFileNames(*prevHostName, name->Left(hostLen)) == 0)
222 pair.HostIndex = prevHostFile;
223 }
224 }
225 else
226 {
227 prevHostFile = (int)updatePairs.Size();
228 prevHostName = name;
229 }
230
231 updatePairs.Add(pair);
232 }
233
234 updatePairs.ReserveDown();
235}
diff --git a/CPP/7zip/UI/Common/UpdatePair.h b/CPP/7zip/UI/Common/UpdatePair.h
new file mode 100644
index 0000000..296d3b0
--- /dev/null
+++ b/CPP/7zip/UI/Common/UpdatePair.h
@@ -0,0 +1,27 @@
1// UpdatePair.h
2
3#ifndef __UPDATE_PAIR_H
4#define __UPDATE_PAIR_H
5
6#include "DirItem.h"
7#include "UpdateAction.h"
8
9#include "../../Archive/IArchive.h"
10
11struct CUpdatePair
12{
13 NUpdateArchive::NPairState::EEnum State;
14 int ArcIndex;
15 int DirIndex;
16 int HostIndex; // >= 0 for alt streams only, contains index of host pair
17
18 CUpdatePair(): ArcIndex(-1), DirIndex(-1), HostIndex(-1) {}
19};
20
21void GetUpdatePairInfoList(
22 const CDirItems &dirItems,
23 const CObjectVector<CArcItem> &arcItems,
24 NFileTimeType::EEnum fileTimeType,
25 CRecordVector<CUpdatePair> &updatePairs);
26
27#endif
diff --git a/CPP/7zip/UI/Common/UpdateProduce.cpp b/CPP/7zip/UI/Common/UpdateProduce.cpp
new file mode 100644
index 0000000..fa4bd69
--- /dev/null
+++ b/CPP/7zip/UI/Common/UpdateProduce.cpp
@@ -0,0 +1,70 @@
1// UpdateProduce.cpp
2
3#include "StdAfx.h"
4
5#include "UpdateProduce.h"
6
7using namespace NUpdateArchive;
8
9static const char * const kUpdateActionSetCollision = "Internal collision in update action set";
10
11void UpdateProduce(
12 const CRecordVector<CUpdatePair> &updatePairs,
13 const CActionSet &actionSet,
14 CRecordVector<CUpdatePair2> &operationChain,
15 IUpdateProduceCallback *callback)
16{
17 FOR_VECTOR (i, updatePairs)
18 {
19 const CUpdatePair &pair = updatePairs[i];
20
21 CUpdatePair2 up2;
22 up2.DirIndex = pair.DirIndex;
23 up2.ArcIndex = pair.ArcIndex;
24 up2.NewData = up2.NewProps = true;
25 up2.UseArcProps = false;
26
27 switch (actionSet.StateActions[(unsigned)pair.State])
28 {
29 case NPairAction::kIgnore:
30 if (pair.ArcIndex >= 0 && callback)
31 callback->ShowDeleteFile((unsigned)pair.ArcIndex);
32 continue;
33
34 case NPairAction::kCopy:
35 if (pair.State == NPairState::kOnlyOnDisk)
36 throw kUpdateActionSetCollision;
37 if (pair.State == NPairState::kOnlyInArchive)
38 {
39 if (pair.HostIndex >= 0)
40 {
41 /*
42 ignore alt stream if
43 1) no such alt stream in Disk
44 2) there is Host file in disk
45 */
46 if (updatePairs[(unsigned)pair.HostIndex].DirIndex >= 0)
47 continue;
48 }
49 }
50 up2.NewData = up2.NewProps = false;
51 up2.UseArcProps = true;
52 break;
53
54 case NPairAction::kCompress:
55 if (pair.State == NPairState::kOnlyInArchive ||
56 pair.State == NPairState::kNotMasked)
57 throw kUpdateActionSetCollision;
58 break;
59
60 case NPairAction::kCompressAsAnti:
61 up2.IsAnti = true;
62 up2.UseArcProps = (pair.ArcIndex >= 0);
63 break;
64 }
65
66 operationChain.Add(up2);
67 }
68
69 operationChain.ReserveDown();
70}
diff --git a/CPP/7zip/UI/Common/UpdateProduce.h b/CPP/7zip/UI/Common/UpdateProduce.h
new file mode 100644
index 0000000..595370f
--- /dev/null
+++ b/CPP/7zip/UI/Common/UpdateProduce.h
@@ -0,0 +1,55 @@
1// UpdateProduce.h
2
3#ifndef __UPDATE_PRODUCE_H
4#define __UPDATE_PRODUCE_H
5
6#include "UpdatePair.h"
7
8struct CUpdatePair2
9{
10 bool NewData;
11 bool NewProps;
12 bool UseArcProps; // if (UseArcProps && NewProps), we want to change only some properties.
13 bool IsAnti; // if (!IsAnti) we use other ways to detect Anti status
14
15 int DirIndex;
16 int ArcIndex;
17 int NewNameIndex;
18
19 bool IsMainRenameItem;
20
21 void SetAs_NoChangeArcItem(unsigned arcIndex) // int
22 {
23 NewData = NewProps = false;
24 UseArcProps = true;
25 IsAnti = false;
26 ArcIndex = (int)arcIndex;
27 }
28
29 bool ExistOnDisk() const { return DirIndex != -1; }
30 bool ExistInArchive() const { return ArcIndex != -1; }
31
32 CUpdatePair2():
33 NewData(false),
34 NewProps(false),
35 UseArcProps(false),
36 IsAnti(false),
37 DirIndex(-1),
38 ArcIndex(-1),
39 NewNameIndex(-1),
40 IsMainRenameItem(false)
41 {}
42};
43
44struct IUpdateProduceCallback
45{
46 virtual HRESULT ShowDeleteFile(unsigned arcIndex) = 0;
47};
48
49void UpdateProduce(
50 const CRecordVector<CUpdatePair> &updatePairs,
51 const NUpdateArchive::CActionSet &actionSet,
52 CRecordVector<CUpdatePair2> &operationChain,
53 IUpdateProduceCallback *callback);
54
55#endif
diff --git a/CPP/7zip/UI/Common/WorkDir.cpp b/CPP/7zip/UI/Common/WorkDir.cpp
new file mode 100644
index 0000000..1307cee
--- /dev/null
+++ b/CPP/7zip/UI/Common/WorkDir.cpp
@@ -0,0 +1,93 @@
1// WorkDir.cpp
2
3#include "StdAfx.h"
4
5#include "../../../Common/StringConvert.h"
6#include "../../../Common/Wildcard.h"
7
8#include "../../../Windows/FileFind.h"
9#include "../../../Windows/FileName.h"
10
11#include "WorkDir.h"
12
13using namespace NWindows;
14using namespace NFile;
15using namespace NDir;
16
17FString GetWorkDir(const NWorkDir::CInfo &workDirInfo, const FString &path, FString &fileName)
18{
19 NWorkDir::NMode::EEnum mode = workDirInfo.Mode;
20
21 #if defined(_WIN32) && !defined(UNDER_CE)
22 if (workDirInfo.ForRemovableOnly)
23 {
24 mode = NWorkDir::NMode::kCurrent;
25 FString prefix = path.Left(3);
26 if (prefix[1] == FTEXT(':') && prefix[2] == FTEXT('\\'))
27 {
28 UINT driveType = GetDriveType(GetSystemString(prefix, ::AreFileApisANSI() ? CP_ACP : CP_OEMCP));
29 if (driveType == DRIVE_CDROM || driveType == DRIVE_REMOVABLE)
30 mode = workDirInfo.Mode;
31 }
32 /*
33 CParsedPath parsedPath;
34 parsedPath.ParsePath(archiveName);
35 UINT driveType = GetDriveType(parsedPath.Prefix);
36 if ((driveType != DRIVE_CDROM) && (driveType != DRIVE_REMOVABLE))
37 mode = NZipSettings::NWorkDir::NMode::kCurrent;
38 */
39 }
40 #endif
41
42 int pos = path.ReverseFind_PathSepar() + 1;
43 fileName = path.Ptr((unsigned)pos);
44
45 switch (mode)
46 {
47 case NWorkDir::NMode::kCurrent:
48 {
49 return path.Left((unsigned)pos);
50 }
51 case NWorkDir::NMode::kSpecified:
52 {
53 FString tempDir = workDirInfo.Path;
54 NName::NormalizeDirPathPrefix(tempDir);
55 return tempDir;
56 }
57 default:
58 {
59 FString tempDir;
60 if (!MyGetTempPath(tempDir))
61 throw 141717;
62 return tempDir;
63 }
64 }
65}
66
67HRESULT CWorkDirTempFile::CreateTempFile(const FString &originalPath)
68{
69 NWorkDir::CInfo workDirInfo;
70 workDirInfo.Load();
71 FString namePart;
72 FString workDir = GetWorkDir(workDirInfo, originalPath, namePart);
73 CreateComplexDir(workDir);
74 CTempFile tempFile;
75 _outStreamSpec = new COutFileStream;
76 OutStream = _outStreamSpec;
77 if (!_tempFile.Create(workDir + namePart, &_outStreamSpec->File))
78 {
79 return GetLastError_noZero_HRESULT();
80 }
81 _originalPath = originalPath;
82 return S_OK;
83}
84
85HRESULT CWorkDirTempFile::MoveToOriginal(bool deleteOriginal)
86{
87 OutStream.Release();
88 if (!_tempFile.MoveTo(_originalPath, deleteOriginal))
89 {
90 return GetLastError_noZero_HRESULT();
91 }
92 return S_OK;
93}
diff --git a/CPP/7zip/UI/Common/WorkDir.h b/CPP/7zip/UI/Common/WorkDir.h
new file mode 100644
index 0000000..75850a9
--- /dev/null
+++ b/CPP/7zip/UI/Common/WorkDir.h
@@ -0,0 +1,26 @@
1// WorkDir.h
2
3#ifndef __WORK_DIR_H
4#define __WORK_DIR_H
5
6#include "../../../Windows/FileDir.h"
7
8#include "../../Common/FileStreams.h"
9
10#include "ZipRegistry.h"
11
12FString GetWorkDir(const NWorkDir::CInfo &workDirInfo, const FString &path, FString &fileName);
13
14class CWorkDirTempFile
15{
16 FString _originalPath;
17 NWindows::NFile::NDir::CTempFile _tempFile;
18 COutFileStream *_outStreamSpec;
19public:
20 CMyComPtr<IOutStream> OutStream;
21
22 HRESULT CreateTempFile(const FString &originalPath);
23 HRESULT MoveToOriginal(bool deleteOriginal);
24};
25
26#endif
diff --git a/CPP/7zip/UI/Common/ZipRegistry.cpp b/CPP/7zip/UI/Common/ZipRegistry.cpp
new file mode 100644
index 0000000..ab4871f
--- /dev/null
+++ b/CPP/7zip/UI/Common/ZipRegistry.cpp
@@ -0,0 +1,521 @@
1// ZipRegistry.cpp
2
3#include "StdAfx.h"
4
5#include "../../../../C/CpuArch.h"
6
7#include "../../../Common/IntToString.h"
8#include "../../../Common/StringToInt.h"
9
10#include "../../../Windows/FileDir.h"
11#include "../../../Windows/Registry.h"
12#include "../../../Windows/Synchronization.h"
13
14#include "ZipRegistry.h"
15
16using namespace NWindows;
17using namespace NRegistry;
18
19static NSynchronization::CCriticalSection g_CS;
20#define CS_LOCK NSynchronization::CCriticalSectionLock lock(g_CS);
21
22static LPCTSTR const kCuPrefix = TEXT("Software") TEXT(STRING_PATH_SEPARATOR) TEXT("7-Zip") TEXT(STRING_PATH_SEPARATOR);
23
24static CSysString GetKeyPath(LPCTSTR path) { return kCuPrefix + (CSysString)path; }
25
26static LONG OpenMainKey(CKey &key, LPCTSTR keyName)
27{
28 return key.Open(HKEY_CURRENT_USER, GetKeyPath(keyName), KEY_READ);
29}
30
31static LONG CreateMainKey(CKey &key, LPCTSTR keyName)
32{
33 return key.Create(HKEY_CURRENT_USER, GetKeyPath(keyName));
34}
35
36static void Key_Set_BoolPair(CKey &key, LPCTSTR name, const CBoolPair &b)
37{
38 if (b.Def)
39 key.SetValue(name, b.Val);
40}
41
42static void Key_Get_BoolPair(CKey &key, LPCTSTR name, CBoolPair &b)
43{
44 b.Val = false;
45 b.Def = (key.GetValue_IfOk(name, b.Val) == ERROR_SUCCESS);
46}
47
48static void Key_Get_BoolPair_true(CKey &key, LPCTSTR name, CBoolPair &b)
49{
50 b.Val = true;
51 b.Def = (key.GetValue_IfOk(name, b.Val) == ERROR_SUCCESS);
52}
53
54namespace NExtract
55{
56
57static LPCTSTR const kKeyName = TEXT("Extraction");
58
59static LPCTSTR const kExtractMode = TEXT("ExtractMode");
60static LPCTSTR const kOverwriteMode = TEXT("OverwriteMode");
61static LPCTSTR const kShowPassword = TEXT("ShowPassword");
62static LPCTSTR const kPathHistory = TEXT("PathHistory");
63static LPCTSTR const kSplitDest = TEXT("SplitDest");
64static LPCTSTR const kElimDup = TEXT("ElimDup");
65// static LPCTSTR const kAltStreams = TEXT("AltStreams");
66static LPCTSTR const kNtSecur = TEXT("Security");
67
68void CInfo::Save() const
69{
70 CS_LOCK
71 CKey key;
72 CreateMainKey(key, kKeyName);
73
74 if (PathMode_Force)
75 key.SetValue(kExtractMode, (UInt32)PathMode);
76 if (OverwriteMode_Force)
77 key.SetValue(kOverwriteMode, (UInt32)OverwriteMode);
78
79 Key_Set_BoolPair(key, kSplitDest, SplitDest);
80 Key_Set_BoolPair(key, kElimDup, ElimDup);
81 // Key_Set_BoolPair(key, kAltStreams, AltStreams);
82 Key_Set_BoolPair(key, kNtSecur, NtSecurity);
83 Key_Set_BoolPair(key, kShowPassword, ShowPassword);
84
85 key.RecurseDeleteKey(kPathHistory);
86 key.SetValue_Strings(kPathHistory, Paths);
87}
88
89void Save_ShowPassword(bool showPassword)
90{
91 CS_LOCK
92 CKey key;
93 CreateMainKey(key, kKeyName);
94 key.SetValue(kShowPassword, showPassword);
95}
96
97void CInfo::Load()
98{
99 PathMode = NPathMode::kCurPaths;
100 PathMode_Force = false;
101 OverwriteMode = NOverwriteMode::kAsk;
102 OverwriteMode_Force = false;
103
104 SplitDest.Val = true;
105
106 Paths.Clear();
107
108 CS_LOCK
109 CKey key;
110 if (OpenMainKey(key, kKeyName) != ERROR_SUCCESS)
111 return;
112
113 key.GetValue_Strings(kPathHistory, Paths);
114 UInt32 v;
115 if (key.QueryValue(kExtractMode, v) == ERROR_SUCCESS && v <= NPathMode::kAbsPaths)
116 {
117 PathMode = (NPathMode::EEnum)v;
118 PathMode_Force = true;
119 }
120 if (key.QueryValue(kOverwriteMode, v) == ERROR_SUCCESS && v <= NOverwriteMode::kRenameExisting)
121 {
122 OverwriteMode = (NOverwriteMode::EEnum)v;
123 OverwriteMode_Force = true;
124 }
125
126 Key_Get_BoolPair_true(key, kSplitDest, SplitDest);
127
128 Key_Get_BoolPair(key, kElimDup, ElimDup);
129 // Key_Get_BoolPair(key, kAltStreams, AltStreams);
130 Key_Get_BoolPair(key, kNtSecur, NtSecurity);
131 Key_Get_BoolPair(key, kShowPassword, ShowPassword);
132}
133
134bool Read_ShowPassword()
135{
136 CS_LOCK
137 CKey key;
138 bool showPassword = false;
139 if (OpenMainKey(key, kKeyName) != ERROR_SUCCESS)
140 return showPassword;
141 key.GetValue_IfOk(kShowPassword, showPassword);
142 return showPassword;
143}
144
145}
146
147namespace NCompression
148{
149
150static LPCTSTR const kKeyName = TEXT("Compression");
151
152static LPCTSTR const kArcHistory = TEXT("ArcHistory");
153static LPCWSTR const kArchiver = L"Archiver";
154static LPCTSTR const kShowPassword = TEXT("ShowPassword");
155static LPCTSTR const kEncryptHeaders = TEXT("EncryptHeaders");
156
157static LPCTSTR const kOptionsKeyName = TEXT("Options");
158
159static LPCTSTR const kLevel = TEXT("Level");
160static LPCTSTR const kDictionary = TEXT("Dictionary");
161static LPCTSTR const kOrder = TEXT("Order");
162static LPCTSTR const kBlockSize = TEXT("BlockSize");
163static LPCTSTR const kNumThreads = TEXT("NumThreads");
164static LPCWSTR const kMethod = L"Method";
165static LPCWSTR const kOptions = L"Options";
166static LPCWSTR const kEncryptionMethod = L"EncryptionMethod";
167
168static LPCTSTR const kNtSecur = TEXT("Security");
169static LPCTSTR const kAltStreams = TEXT("AltStreams");
170static LPCTSTR const kHardLinks = TEXT("HardLinks");
171static LPCTSTR const kSymLinks = TEXT("SymLinks");
172
173static void SetRegString(CKey &key, LPCWSTR name, const UString &value)
174{
175 if (value.IsEmpty())
176 key.DeleteValue(name);
177 else
178 key.SetValue(name, value);
179}
180
181static void SetRegUInt32(CKey &key, LPCTSTR name, UInt32 value)
182{
183 if (value == (UInt32)(Int32)-1)
184 key.DeleteValue(name);
185 else
186 key.SetValue(name, value);
187}
188
189static void GetRegString(CKey &key, LPCWSTR name, UString &value)
190{
191 if (key.QueryValue(name, value) != ERROR_SUCCESS)
192 value.Empty();
193}
194
195static void GetRegUInt32(CKey &key, LPCTSTR name, UInt32 &value)
196{
197 if (key.QueryValue(name, value) != ERROR_SUCCESS)
198 value = (UInt32)(Int32)-1;
199}
200
201static LPCWSTR const kMemUse = L"MemUse"
202 #if defined(MY_CPU_SIZEOF_POINTER) && (MY_CPU_SIZEOF_POINTER == 4)
203 L"32";
204 #else
205 L"64";
206 #endif
207
208void CInfo::Save() const
209{
210 CS_LOCK
211
212 CKey key;
213 CreateMainKey(key, kKeyName);
214
215 Key_Set_BoolPair(key, kNtSecur, NtSecurity);
216 Key_Set_BoolPair(key, kAltStreams, AltStreams);
217 Key_Set_BoolPair(key, kHardLinks, HardLinks);
218 Key_Set_BoolPair(key, kSymLinks, SymLinks);
219
220 key.SetValue(kShowPassword, ShowPassword);
221 key.SetValue(kLevel, (UInt32)Level);
222 key.SetValue(kArchiver, ArcType);
223 key.SetValue(kShowPassword, ShowPassword);
224 key.SetValue(kEncryptHeaders, EncryptHeaders);
225 key.RecurseDeleteKey(kArcHistory);
226 key.SetValue_Strings(kArcHistory, ArcPaths);
227
228 key.RecurseDeleteKey(kOptionsKeyName);
229 {
230 CKey optionsKey;
231 optionsKey.Create(key, kOptionsKeyName);
232 FOR_VECTOR (i, Formats)
233 {
234 const CFormatOptions &fo = Formats[i];
235 CKey fk;
236 fk.Create(optionsKey, fo.FormatID);
237
238 SetRegUInt32(fk, kLevel, fo.Level);
239 SetRegUInt32(fk, kDictionary, fo.Dictionary);
240 SetRegUInt32(fk, kOrder, fo.Order);
241 SetRegUInt32(fk, kBlockSize, fo.BlockLogSize);
242 SetRegUInt32(fk, kNumThreads, fo.NumThreads);
243
244 SetRegString(fk, kMethod, fo.Method);
245 SetRegString(fk, kOptions, fo.Options);
246 SetRegString(fk, kEncryptionMethod, fo.EncryptionMethod);
247 SetRegString(fk, kMemUse, fo.MemUse);
248 }
249 }
250}
251
252void CInfo::Load()
253{
254 ArcPaths.Clear();
255 Formats.Clear();
256
257 Level = 5;
258 ArcType = L"7z";
259 ShowPassword = false;
260 EncryptHeaders = false;
261
262 CS_LOCK
263 CKey key;
264
265 if (OpenMainKey(key, kKeyName) != ERROR_SUCCESS)
266 return;
267
268 Key_Get_BoolPair(key, kNtSecur, NtSecurity);
269 Key_Get_BoolPair(key, kAltStreams, AltStreams);
270 Key_Get_BoolPair(key, kHardLinks, HardLinks);
271 Key_Get_BoolPair(key, kSymLinks, SymLinks);
272
273 key.GetValue_Strings(kArcHistory, ArcPaths);
274
275 {
276 CKey optionsKey;
277 if (optionsKey.Open(key, kOptionsKeyName, KEY_READ) == ERROR_SUCCESS)
278 {
279 CSysStringVector formatIDs;
280 optionsKey.EnumKeys(formatIDs);
281 FOR_VECTOR (i, formatIDs)
282 {
283 CKey fk;
284 CFormatOptions fo;
285 fo.FormatID = formatIDs[i];
286 if (fk.Open(optionsKey, fo.FormatID, KEY_READ) == ERROR_SUCCESS)
287 {
288 GetRegString(fk, kMethod, fo.Method);
289 GetRegString(fk, kOptions, fo.Options);
290 GetRegString(fk, kEncryptionMethod, fo.EncryptionMethod);
291 GetRegString(fk, kMemUse, fo.MemUse);
292
293 GetRegUInt32(fk, kLevel, fo.Level);
294 GetRegUInt32(fk, kDictionary, fo.Dictionary);
295 GetRegUInt32(fk, kOrder, fo.Order);
296 GetRegUInt32(fk, kBlockSize, fo.BlockLogSize);
297 GetRegUInt32(fk, kNumThreads, fo.NumThreads);
298
299 Formats.Add(fo);
300 }
301 }
302 }
303 }
304
305 UString a;
306 if (key.QueryValue(kArchiver, a) == ERROR_SUCCESS)
307 ArcType = a;
308 key.GetValue_IfOk(kLevel, Level);
309 key.GetValue_IfOk(kShowPassword, ShowPassword);
310 key.GetValue_IfOk(kEncryptHeaders, EncryptHeaders);
311}
312
313
314static bool ParseMemUse(const wchar_t *s, CMemUse &mu)
315{
316 mu.Clear();
317
318 bool percentMode = false;
319 {
320 const wchar_t c = *s;
321 if (MyCharLower_Ascii(c) == 'p')
322 {
323 percentMode = true;
324 s++;
325 }
326 }
327 const wchar_t *end;
328 UInt64 number = ConvertStringToUInt64(s, &end);
329 if (end == s)
330 return false;
331
332 wchar_t c = *end;
333
334 if (percentMode)
335 {
336 if (c != 0)
337 return false;
338 mu.IsPercent = true;
339 mu.Val = number;
340 return true;
341 }
342
343 if (c == 0)
344 {
345 mu.Val = number;
346 return true;
347 }
348
349 c = MyCharLower_Ascii(c);
350
351 const wchar_t c1 = end[1];
352
353 if (c == '%')
354 {
355 if (c1 != 0)
356 return false;
357 mu.IsPercent = true;
358 mu.Val = number;
359 return true;
360 }
361
362 if (c == 'b')
363 {
364 if (c1 != 0)
365 return false;
366 mu.Val = number;
367 return true;
368 }
369
370 if (c1 != 0)
371 if (MyCharLower_Ascii(c1) != 'b' || end[2] != 0)
372 return false;
373
374 unsigned numBits;
375 switch (c)
376 {
377 case 'k': numBits = 10; break;
378 case 'm': numBits = 20; break;
379 case 'g': numBits = 30; break;
380 case 't': numBits = 40; break;
381 default: return false;
382 }
383 if (number >= ((UInt64)1 << (64 - numBits)))
384 return false;
385 mu.Val = number << numBits;
386 return true;
387}
388
389
390void CMemUse::Parse(const UString &s)
391{
392 IsDefined = ParseMemUse(s, *this);
393}
394
395/*
396void MemLimit_Save(const UString &s)
397{
398 CS_LOCK
399 CKey key;
400 CreateMainKey(key, kKeyName);
401 SetRegString(key, kMemUse, s);
402}
403
404bool MemLimit_Load(NCompression::CMemUse &mu)
405{
406 mu.Clear();
407 UString a;
408 {
409 CS_LOCK
410 CKey key;
411 if (OpenMainKey(key, kKeyName) != ERROR_SUCCESS)
412 return false;
413 if (key.QueryValue(kMemUse, a) != ERROR_SUCCESS)
414 return false;
415 }
416 if (a.IsEmpty())
417 return false;
418 mu.Parse(a);
419 return mu.IsDefined;
420}
421*/
422
423}
424
425static LPCTSTR const kOptionsInfoKeyName = TEXT("Options");
426
427namespace NWorkDir
428{
429static LPCTSTR const kWorkDirType = TEXT("WorkDirType");
430static LPCWSTR const kWorkDirPath = L"WorkDirPath";
431static LPCTSTR const kTempRemovableOnly = TEXT("TempRemovableOnly");
432
433
434void CInfo::Save()const
435{
436 CS_LOCK
437 CKey key;
438 CreateMainKey(key, kOptionsInfoKeyName);
439 key.SetValue(kWorkDirType, (UInt32)Mode);
440 key.SetValue(kWorkDirPath, fs2us(Path));
441 key.SetValue(kTempRemovableOnly, ForRemovableOnly);
442}
443
444void CInfo::Load()
445{
446 SetDefault();
447
448 CS_LOCK
449 CKey key;
450 if (OpenMainKey(key, kOptionsInfoKeyName) != ERROR_SUCCESS)
451 return;
452
453 UInt32 dirType;
454 if (key.QueryValue(kWorkDirType, dirType) != ERROR_SUCCESS)
455 return;
456 switch (dirType)
457 {
458 case NMode::kSystem:
459 case NMode::kCurrent:
460 case NMode::kSpecified:
461 Mode = (NMode::EEnum)dirType;
462 }
463 UString pathU;
464 if (key.QueryValue(kWorkDirPath, pathU) == ERROR_SUCCESS)
465 Path = us2fs(pathU);
466 else
467 {
468 Path.Empty();
469 if (Mode == NMode::kSpecified)
470 Mode = NMode::kSystem;
471 }
472 key.GetValue_IfOk(kTempRemovableOnly, ForRemovableOnly);
473}
474
475}
476
477static LPCTSTR const kCascadedMenu = TEXT("CascadedMenu");
478static LPCTSTR const kContextMenu = TEXT("ContextMenu");
479static LPCTSTR const kMenuIcons = TEXT("MenuIcons");
480static LPCTSTR const kElimDup = TEXT("ElimDupExtract");
481
482void CContextMenuInfo::Save() const
483{
484 CS_LOCK
485 CKey key;
486 CreateMainKey(key, kOptionsInfoKeyName);
487
488 Key_Set_BoolPair(key, kCascadedMenu, Cascaded);
489 Key_Set_BoolPair(key, kMenuIcons, MenuIcons);
490 Key_Set_BoolPair(key, kElimDup, ElimDup);
491
492 if (Flags_Def)
493 key.SetValue(kContextMenu, Flags);
494}
495
496void CContextMenuInfo::Load()
497{
498 Cascaded.Val = true;
499 Cascaded.Def = false;
500
501 MenuIcons.Val = false;
502 MenuIcons.Def = false;
503
504 ElimDup.Val = true;
505 ElimDup.Def = false;
506
507 Flags = (UInt32)(Int32)-1;
508 Flags_Def = false;
509
510 CS_LOCK
511
512 CKey key;
513 if (OpenMainKey(key, kOptionsInfoKeyName) != ERROR_SUCCESS)
514 return;
515
516 Key_Get_BoolPair_true(key, kCascadedMenu, Cascaded);
517 Key_Get_BoolPair_true(key, kElimDup, ElimDup);
518 Key_Get_BoolPair(key, kMenuIcons, MenuIcons);
519
520 Flags_Def = (key.GetValue_IfOk(kContextMenu, Flags) == ERROR_SUCCESS);
521}
diff --git a/CPP/7zip/UI/Common/ZipRegistry.h b/CPP/7zip/UI/Common/ZipRegistry.h
new file mode 100644
index 0000000..3d2e4b9
--- /dev/null
+++ b/CPP/7zip/UI/Common/ZipRegistry.h
@@ -0,0 +1,163 @@
1// ZipRegistry.h
2
3#ifndef __ZIP_REGISTRY_H
4#define __ZIP_REGISTRY_H
5
6#include "../../../Common/MyTypes.h"
7#include "../../../Common/MyString.h"
8
9#include "../../Common/MethodProps.h"
10
11#include "ExtractMode.h"
12
13namespace NExtract
14{
15 struct CInfo
16 {
17 NPathMode::EEnum PathMode;
18 NOverwriteMode::EEnum OverwriteMode;
19 bool PathMode_Force;
20 bool OverwriteMode_Force;
21
22 CBoolPair SplitDest;
23 CBoolPair ElimDup;
24 // CBoolPair AltStreams;
25 CBoolPair NtSecurity;
26 CBoolPair ShowPassword;
27
28 UStringVector Paths;
29
30 void Save() const;
31 void Load();
32 };
33
34 void Save_ShowPassword(bool showPassword);
35 bool Read_ShowPassword();
36}
37
38namespace NCompression
39{
40 struct CMemUse
41 {
42 // UString Str;
43 bool IsDefined;
44 bool IsPercent;
45 UInt64 Val;
46
47 CMemUse():
48 IsDefined(false),
49 IsPercent(false),
50 Val(0)
51 {}
52
53 void Clear()
54 {
55 // Str.Empty();
56 IsDefined = false;
57 IsPercent = false;
58 Val = 0;
59 }
60
61 UInt64 GetBytes(UInt64 ramSize) const
62 {
63 if (!IsPercent)
64 return Val;
65 return Calc_From_Val_Percents(ramSize, Val);
66 }
67 void Parse(const UString &s);
68 };
69
70 struct CFormatOptions
71 {
72 UInt32 Level;
73 UInt32 Dictionary;
74 UInt32 Order;
75 UInt32 BlockLogSize;
76 UInt32 NumThreads;
77
78 CSysString FormatID;
79 UString Method;
80 UString Options;
81 UString EncryptionMethod;
82 UString MemUse;
83
84 void Reset_BlockLogSize()
85 {
86 BlockLogSize = (UInt32)(Int32)-1;
87 }
88
89 void ResetForLevelChange()
90 {
91 BlockLogSize = NumThreads = Level = Dictionary = Order = (UInt32)(Int32)-1;
92 Method.Empty();
93 // Options.Empty();
94 // EncryptionMethod.Empty();
95 }
96 CFormatOptions() { ResetForLevelChange(); }
97 };
98
99 struct CInfo
100 {
101 UInt32 Level;
102 bool ShowPassword;
103 bool EncryptHeaders;
104 UString ArcType;
105 UStringVector ArcPaths;
106
107 CObjectVector<CFormatOptions> Formats;
108
109 CBoolPair NtSecurity;
110 CBoolPair AltStreams;
111 CBoolPair HardLinks;
112 CBoolPair SymLinks;
113
114 void Save() const;
115 void Load();
116 };
117}
118
119namespace NWorkDir
120{
121 namespace NMode
122 {
123 enum EEnum
124 {
125 kSystem,
126 kCurrent,
127 kSpecified
128 };
129 }
130 struct CInfo
131 {
132 NMode::EEnum Mode;
133 FString Path;
134 bool ForRemovableOnly;
135
136 void SetForRemovableOnlyDefault() { ForRemovableOnly = true; }
137 void SetDefault()
138 {
139 Mode = NMode::kSystem;
140 Path.Empty();
141 SetForRemovableOnlyDefault();
142 }
143
144 void Save() const;
145 void Load();
146 };
147}
148
149
150struct CContextMenuInfo
151{
152 CBoolPair Cascaded;
153 CBoolPair MenuIcons;
154 CBoolPair ElimDup;
155
156 bool Flags_Def;
157 UInt32 Flags;
158
159 void Save() const;
160 void Load();
161};
162
163#endif