diff options
Diffstat (limited to 'CPP/7zip/UI/Common')
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 | |||
42 | extern bool g_CaseSensitive; | ||
43 | extern bool g_PathTrailReplaceMode; | ||
44 | |||
45 | #ifdef _7ZIP_LARGE_PAGES | ||
46 | extern | ||
47 | bool g_LargePagesMode; | ||
48 | bool g_LargePagesMode = false; | ||
49 | #endif | ||
50 | |||
51 | /* | ||
52 | #ifdef ENV_HAVE_LSTAT | ||
53 | EXTERN_C_BEGIN | ||
54 | extern int global_use_lstat; | ||
55 | EXTERN_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); | ||
67 | static 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 | |||
80 | using namespace NCommandLineParser; | ||
81 | using namespace NWindows; | ||
82 | using namespace NFile; | ||
83 | |||
84 | static 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 | |||
94 | namespace NKey { | ||
95 | enum 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 | |||
181 | static const wchar_t kRecursedIDChar = 'r'; | ||
182 | static const char * const kRecursedPostCharSet = "0-"; | ||
183 | |||
184 | static const char * const k_ArcNameMode_PostCharSet = "sea"; | ||
185 | |||
186 | static const char * const k_Stream_PostCharSet = "012"; | ||
187 | |||
188 | static 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 | |||
198 | namespace 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 | |||
213 | static const Byte kSomeCludePostStringMinSize = 2; // at least <@|!><N>ame must be | ||
214 | static const Byte kSomeCludeAfterRecursedPostStringMinSize = 2; // at least <@|!><N>ame must be | ||
215 | |||
216 | static const char * const kOverwritePostCharSet = "asut"; | ||
217 | |||
218 | static 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 | |||
239 | static 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 | |||
322 | static const char * const kUniversalWildcard = "*"; | ||
323 | static const unsigned kMinNonSwitchWords = 1; | ||
324 | static const unsigned kCommandIndex = 0; | ||
325 | |||
326 | // static const char * const kUserErrorMessage = "Incorrect command line"; | ||
327 | // static const char * const kCannotFindListFile = "Cannot find listfile"; | ||
328 | static const char * const kIncorrectListFile = "Incorrect item in listfile.\nCheck charset encoding and -scs switch."; | ||
329 | static const char * const kTerminalOutError = "I won't write compressed data to a terminal"; | ||
330 | static const char * const kSameTerminalError = "I won't write data and program's messages to same stream"; | ||
331 | static const char * const kEmptyFilePath = "Empty file path"; | ||
332 | |||
333 | bool 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 | |||
346 | NExtract::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 | |||
358 | bool 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 | |||
372 | static 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 | |||
385 | static const char *g_Commands = "audtexlbih"; | ||
386 | |||
387 | static 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 | |||
412 | struct 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 | |||
428 | static 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 | |||
452 | static 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 | |||
477 | static 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 | |||
515 | static 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 | |||
579 | struct 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 | |||
592 | static const char * const k_IncorrectMapCommand = "Incorrect Map command"; | ||
593 | |||
594 | static 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 | |||
650 | static 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 | /* | ||
796 | static 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 | |||
809 | static const char * const kUpdatePairStateIDSet = "pqrxyzw"; | ||
810 | static const int kUpdatePairStateNotSupportedActions[] = {2, 2, 1, -1, -1, -1, -1}; | ||
811 | |||
812 | static const unsigned kNumUpdatePairActions = 4; | ||
813 | static const char * const kUpdateIgnoreItselfPostStringID = "-"; | ||
814 | static const wchar_t kUpdateNewArchivePostCharID = '!'; | ||
815 | |||
816 | |||
817 | static 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 | |||
845 | static 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 | |||
892 | bool ParseComplexSize(const wchar_t *s, UInt64 &result); | ||
893 | |||
894 | static 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 | |||
946 | static 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 | |||
966 | static 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) | ||
974 | static void PrintHex(UString &s, UInt64 v) | ||
975 | { | ||
976 | char temp[32]; | ||
977 | ConvertUInt64ToHex(v, temp); | ||
978 | s += temp; | ||
979 | } | ||
980 | #endif | ||
981 | |||
982 | |||
983 | void 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 | |||
1163 | struct CCodePagePair | ||
1164 | { | ||
1165 | const char *Name; | ||
1166 | UInt32 CodePage; | ||
1167 | }; | ||
1168 | |||
1169 | static const unsigned kNumByteOnlyCodePages = 3; | ||
1170 | |||
1171 | static 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 | |||
1180 | static 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 | |||
1204 | static 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 | |||
1211 | void 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 | |||
1632 | static AString g_ModuleDirPrefix; | ||
1633 | |||
1634 | void Set_ModuleDirPrefix_From_ProgArg0(const char *s); | ||
1635 | void 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 | |||
1643 | namespace NWindows { | ||
1644 | namespace NDLL { | ||
1645 | |||
1646 | FString GetModuleDirPrefix(); | ||
1647 | FString 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 | |||
15 | typedef CMessagePathException CArcCmdLineException; | ||
16 | |||
17 | namespace 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 | |||
32 | struct 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 | |||
42 | enum | ||
43 | { | ||
44 | k_OutStream_disabled = 0, | ||
45 | k_OutStream_stdout = 1, | ||
46 | k_OutStream_stderr = 2 | ||
47 | }; | ||
48 | |||
49 | struct 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 | |||
148 | class CArcCmdLineParser | ||
149 | { | ||
150 | NCommandLineParser::CParser parser; | ||
151 | public: | ||
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 | |||
41 | using namespace NWindows; | ||
42 | using namespace NFile; | ||
43 | using namespace NDir; | ||
44 | |||
45 | static const char * const kCantAutoRename = "Cannot create file with auto name"; | ||
46 | static const char * const kCantRenameFile = "Cannot rename existing file"; | ||
47 | static const char * const kCantDeleteOutputFile = "Cannot delete output file"; | ||
48 | static const char * const kCantDeleteOutputDir = "Cannot delete output folder"; | ||
49 | static const char * const kCantOpenOutFile = "Cannot open output file"; | ||
50 | static const char * const kCantOpenInFile = "Cannot open input file"; | ||
51 | static const char * const kCantSetFileLen = "Cannot set length for output file"; | ||
52 | #ifdef SUPPORT_LINKS | ||
53 | static const char * const kCantCreateHardLink = "Cannot create hard link"; | ||
54 | static const char * const kCantCreateSymLink = "Cannot create symbolic link"; | ||
55 | #endif | ||
56 | |||
57 | #ifndef _SFX | ||
58 | |||
59 | STDMETHODIMP 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 | ||
76 | bool InitLocalPrivileges(); | ||
77 | bool 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 | |||
100 | int 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 | |||
107 | static 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 | |||
128 | HRESULT 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 | |||
191 | CArchiveExtractCallback::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 | |||
207 | void 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 | |||
287 | STDMETHODIMP 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 | |||
299 | static 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 | |||
310 | static 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 | |||
320 | STDMETHODIMP 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 | |||
341 | STDMETHODIMP 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 | |||
349 | void 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 | |||
419 | HRESULT 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 | |||
436 | HRESULT CArchiveExtractCallback::GetUnpackSize() | ||
437 | { | ||
438 | return _arc->GetItemSize(_index, _curSize, _curSizeDefined); | ||
439 | } | ||
440 | |||
441 | static void AddPathToMessage(UString &s, const FString &path) | ||
442 | { | ||
443 | s += " : "; | ||
444 | s += fs2us(path); | ||
445 | } | ||
446 | |||
447 | HRESULT CArchiveExtractCallback::SendMessageError(const char *message, const FString &path) | ||
448 | { | ||
449 | UString s (message); | ||
450 | AddPathToMessage(s, path); | ||
451 | return _extractCallback2->MessageError(s); | ||
452 | } | ||
453 | |||
454 | HRESULT 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 | |||
467 | HRESULT 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 | |||
482 | STDMETHODIMP 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 | |||
502 | static 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 | |||
517 | struct CLinkLevelsInfo | ||
518 | { | ||
519 | bool IsAbsolute; | ||
520 | int LowLevel; | ||
521 | int FinalLevel; | ||
522 | |||
523 | void Parse(const UString &path); | ||
524 | }; | ||
525 | |||
526 | void 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 | |||
562 | bool IsSafePath(const UString &path); | ||
563 | bool 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 | |||
573 | bool CensorNode_CheckPath2(const NWildcard::CCensorNode &node, const CReadArcItem &item, bool &include); | ||
574 | bool 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 | |||
619 | bool 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 | |||
628 | static 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 | /* | ||
647 | struct CTempMidBuffer | ||
648 | { | ||
649 | void *Buf; | ||
650 | |||
651 | CTempMidBuffer(size_t size): Buf(NULL) { Buf = ::MidAlloc(size); } | ||
652 | ~CTempMidBuffer() { ::MidFree(Buf); } | ||
653 | }; | ||
654 | |||
655 | HRESULT 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 | |||
685 | HRESULT 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 | |||
847 | HRESULT 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 | |||
885 | void 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 | |||
927 | void 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 | |||
990 | HRESULT 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 | |||
1116 | HRESULT 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 | |||
1360 | HRESULT 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 | |||
1381 | STDMETHODIMP 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 | |||
1682 | STDMETHODIMP 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 | |||
1713 | HRESULT 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 | |||
1752 | HRESULT 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 | |||
1911 | bool 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 | |||
1965 | HRESULT 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 | |||
2066 | void 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 | |||
2094 | STDMETHODIMP 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 | |||
2185 | STDMETHODIMP 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 | |||
2213 | STDMETHODIMP 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 | |||
2228 | FString 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 | |||
2247 | STDMETHODIMP 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 | |||
2266 | STDMETHODIMP 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 | |||
2291 | STDMETHODIMP 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 | |||
2302 | void 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 | |||
2318 | bool CDirPathTime::SetDirTime() const | ||
2319 | { | ||
2320 | return NDir::SetDirTime(Path, | ||
2321 | CTimeDefined ? &CTime : NULL, | ||
2322 | ATimeDefined ? &ATime : NULL, | ||
2323 | MTimeDefined ? &MTime : NULL); | ||
2324 | } | ||
2325 | |||
2326 | |||
2327 | HRESULT 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 | |||
2380 | HRESULT 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 | |||
26 | class COutStreamWithHash: | ||
27 | public ISequentialOutStream, | ||
28 | public CMyUnknownImp | ||
29 | { | ||
30 | CMyComPtr<ISequentialOutStream> _stream; | ||
31 | UInt64 _size; | ||
32 | bool _calculate; | ||
33 | public: | ||
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 | |||
53 | struct 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 | |||
91 | class CGetProp: | ||
92 | public IGetProp, | ||
93 | public CMyUnknownImp | ||
94 | { | ||
95 | public: | ||
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 | |||
117 | struct CHardLinkNode | ||
118 | { | ||
119 | UInt64 StreamId; | ||
120 | UInt64 INode; | ||
121 | |||
122 | int Compare(const CHardLinkNode &a) const; | ||
123 | }; | ||
124 | |||
125 | class CHardLinks | ||
126 | { | ||
127 | public: | ||
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 | |||
148 | struct 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 | |||
166 | struct 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 | |||
184 | struct 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 | |||
219 | class 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 | |||
369 | public: | ||
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 | |||
374 | public: | ||
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 | |||
443 | private: | ||
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 | |||
452 | public: | ||
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 | |||
475 | private: | ||
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 | |||
510 | struct 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 | |||
534 | bool 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 | |||
13 | using namespace NWindows; | ||
14 | using namespace NFile; | ||
15 | |||
16 | static 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 | |||
32 | static 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 | |||
78 | UString 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 | |||
8 | UString 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 | |||
14 | using namespace NWindows; | ||
15 | |||
16 | STDMETHODIMP 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 | |||
27 | STDMETHODIMP 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 | |||
38 | STDMETHODIMP 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 | |||
64 | struct 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 | ||
79 | bool IsSafePath(const UString &path); | ||
80 | |||
81 | STDMETHODIMP 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 | ||
150 | STDMETHODIMP 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 | |||
37 | struct IOpenCallbackUI | ||
38 | { | ||
39 | INTERFACE_IOpenCallbackUI(=0) | ||
40 | }; | ||
41 | |||
42 | class COpenCallbackImp: | ||
43 | public IArchiveOpenCallback, | ||
44 | public IArchiveOpenVolumeCallback, | ||
45 | public IArchiveOpenSetSubArchiveName, | ||
46 | #ifndef _NO_CRYPTO | ||
47 | public ICryptoGetTextPassword, | ||
48 | #endif | ||
49 | public CMyUnknownImp | ||
50 | { | ||
51 | public: | ||
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 | |||
75 | private: | ||
76 | FString _folderPrefix; | ||
77 | NWindows::NFile::NFind::CFileInfo _fileInfo; | ||
78 | bool _subArchiveMode; | ||
79 | UString _subArchiveName; | ||
80 | |||
81 | public: | ||
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 | |||
59 | using namespace NWindows; | ||
60 | |||
61 | #ifndef _7ZIP_ST | ||
62 | static const UInt32 k_LZMA = 0x030101; | ||
63 | #endif | ||
64 | |||
65 | static const UInt64 kComplexInCommands = (UInt64)1 << | ||
66 | #ifdef UNDER_CE | ||
67 | 31; | ||
68 | #else | ||
69 | 34; | ||
70 | #endif | ||
71 | |||
72 | static const UInt32 kComplexInMs = 4000; | ||
73 | |||
74 | static 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 | ||
92 | static const unsigned kBenchmarkUsageMultBits = 16; | ||
93 | static const UInt64 kBenchmarkUsageMult = 1 << kBenchmarkUsageMultBits; | ||
94 | |||
95 | UInt64 Benchmark_GetUsage_Percents(UInt64 usage) | ||
96 | { | ||
97 | return (100 * usage + kBenchmarkUsageMult / 2) / kBenchmarkUsageMult; | ||
98 | } | ||
99 | |||
100 | static const unsigned kNumHashDictBits = 17; | ||
101 | static const UInt32 kFilterUnpackSize = (47 << 10); // + 5; // for test | ||
102 | |||
103 | static const unsigned kOldLzmaDictBits = 32; | ||
104 | |||
105 | // static const size_t kAdditionalSize = (size_t)1 << 32; // for debug | ||
106 | static const size_t kAdditionalSize = (size_t)1 << 16; | ||
107 | static const UInt32 kCompressedAdditionalSize = (1 << 10); | ||
108 | |||
109 | static 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 | |||
117 | class CBaseRandomGenerator | ||
118 | { | ||
119 | UInt32 A1; | ||
120 | UInt32 A2; | ||
121 | UInt32 Salt; | ||
122 | public: | ||
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 | |||
137 | MY_NO_INLINE | ||
138 | static 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 | |||
157 | class 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 | |||
172 | public: | ||
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 | |||
258 | class CBenchmarkInStream: | ||
259 | public ISequentialInStream, | ||
260 | public CMyUnknownImp | ||
261 | { | ||
262 | const Byte *Data; | ||
263 | size_t Pos; | ||
264 | size_t Size; | ||
265 | |||
266 | public: | ||
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 | |||
278 | STDMETHODIMP 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 | |||
296 | class CBenchmarkOutStream: | ||
297 | public ISequentialOutStream, | ||
298 | public CMidAlignedBuffer, | ||
299 | public CMyUnknownImp | ||
300 | { | ||
301 | // bool _overflow; | ||
302 | public: | ||
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 | |||
336 | STDMETHODIMP 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 | |||
360 | class CCrcOutStream: | ||
361 | public ISequentialOutStream, | ||
362 | public CMyUnknownImp | ||
363 | { | ||
364 | public: | ||
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 | |||
380 | STDMETHODIMP 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 | |||
392 | static 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 | |||
411 | static 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 | |||
430 | struct 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 | |||
471 | struct 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 | |||
495 | static inline UInt64 GetTime64(const FILETIME &t) { return ((UInt64)t.dwHighDateTime << 32) | t.dwLowDateTime; } | ||
496 | |||
497 | void 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 | |||
524 | static 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 | |||
534 | class CBenchProgressStatus | ||
535 | { | ||
536 | #ifndef _7ZIP_ST | ||
537 | NSynchronization::CCriticalSection CS; | ||
538 | #endif | ||
539 | public: | ||
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 | |||
558 | struct CBenchInfoCalc | ||
559 | { | ||
560 | CBenchInfo BenchInfo; | ||
561 | CUserTime UserTime; | ||
562 | |||
563 | void SetStartTime(); | ||
564 | void SetFinishTime(CBenchInfo &dest); | ||
565 | }; | ||
566 | |||
567 | void CBenchInfoCalc::SetStartTime() | ||
568 | { | ||
569 | BenchInfo.GlobalFreq = GetFreq(); | ||
570 | BenchInfo.UserFreq = GetUserFreq(); | ||
571 | BenchInfo.GlobalTime = ::GetTimeCount(); | ||
572 | BenchInfo.UserTime = 0; | ||
573 | UserTime.Init(); | ||
574 | } | ||
575 | |||
576 | void CBenchInfoCalc::SetFinishTime(CBenchInfo &dest) | ||
577 | { | ||
578 | dest = BenchInfo; | ||
579 | dest.GlobalTime = ::GetTimeCount() - BenchInfo.GlobalTime; | ||
580 | dest.UserTime = UserTime.GetUserTime(); | ||
581 | } | ||
582 | |||
583 | class CBenchProgressInfo: | ||
584 | public ICompressProgressInfo, | ||
585 | public CMyUnknownImp, | ||
586 | public CBenchInfoCalc | ||
587 | { | ||
588 | public: | ||
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 | |||
598 | STDMETHODIMP 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 | |||
640 | static const unsigned kSubBits = 8; | ||
641 | |||
642 | static 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 | |||
653 | static 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 | |||
667 | static 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 | |||
675 | static 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 | |||
709 | UInt64 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 | |||
734 | UInt64 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 | |||
758 | UInt64 CBenchInfo::GetSpeed(UInt64 numUnits) const | ||
759 | { | ||
760 | return MyMultDiv64(numUnits, GlobalFreq, GlobalTime); | ||
761 | } | ||
762 | |||
763 | struct 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 | |||
797 | void CBenchProps::SetLzmaCompexity() | ||
798 | { | ||
799 | EncComplex = 1200; | ||
800 | DecComplexUnc = 4; | ||
801 | DecComplexCompr = 190; | ||
802 | LzmaRatingMode = true; | ||
803 | } | ||
804 | |||
805 | UInt64 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 | |||
829 | UInt64 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 | |||
837 | UInt64 CBenchInfo::GetRating_LzmaEnc(UInt64 dictSize) const | ||
838 | { | ||
839 | CBenchProps props; | ||
840 | props.SetLzmaCompexity(); | ||
841 | return props.GetCompressRating(dictSize, GlobalTime, GlobalFreq, UnpackSize * NumIterations); | ||
842 | } | ||
843 | |||
844 | UInt64 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 | |||
856 | struct 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 | |||
888 | void 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 | |||
922 | DWORD_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 | |||
979 | struct CBenchSyncCommon | ||
980 | { | ||
981 | bool ExitMode; | ||
982 | NSynchronization::CManualResetEvent StartEvent; | ||
983 | |||
984 | CBenchSyncCommon(): ExitMode(false) {} | ||
985 | }; | ||
986 | |||
987 | #endif | ||
988 | |||
989 | |||
990 | |||
991 | class CEncoderInfo; | ||
992 | |||
993 | class CEncoderInfo | ||
994 | { | ||
995 | CLASS_NO_COPY(CEncoderInfo) | ||
996 | |||
997 | public: | ||
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 | |||
1175 | static size_t GetBenchCompressedSize(size_t bufferSize) | ||
1176 | { | ||
1177 | return kCompressedAdditionalSize + bufferSize + bufferSize / 16; | ||
1178 | // kBufferSize / 2; | ||
1179 | } | ||
1180 | |||
1181 | |||
1182 | HRESULT 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 | |||
1317 | static 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 | |||
1335 | HRESULT 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 | |||
1477 | HRESULT 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 | |||
1635 | static const UInt32 kNumThreadsMax = (1 << 12); | ||
1636 | |||
1637 | struct CBenchEncoders | ||
1638 | { | ||
1639 | CEncoderInfo *encoders; | ||
1640 | CBenchEncoders(UInt32 num): encoders(NULL) { encoders = new CEncoderInfo[num]; } | ||
1641 | ~CBenchEncoders() { delete []encoders; } | ||
1642 | }; | ||
1643 | |||
1644 | |||
1645 | static 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 | |||
1659 | struct 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 | |||
1677 | WRes 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 | |||
1703 | static 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 | |||
1714 | static 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 | |||
2064 | static 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 | |||
2079 | static 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 | |||
2116 | UInt64 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 | |||
2136 | static 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 | |||
2145 | struct 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 | |||
2162 | HRESULT 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 | |||
2179 | HRESULT 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 | |||
2245 | extern | ||
2246 | UInt32 g_BenchCpuFreqTemp; // we need non-static variavble to disable compiler optimization | ||
2247 | UInt32 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 | ||
2253 | static const UInt32 kNumFreqCommands = 128; | ||
2254 | |||
2255 | EXTERN_C_BEGIN | ||
2256 | |||
2257 | static 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 | |||
2266 | EXTERN_C_END | ||
2267 | |||
2268 | |||
2269 | #ifndef _7ZIP_ST | ||
2270 | |||
2271 | struct 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 | |||
2285 | struct CFreqInfo: public CBaseThreadInfo | ||
2286 | { | ||
2287 | UInt32 ValRes; | ||
2288 | UInt32 Size; | ||
2289 | UInt64 NumIterations; | ||
2290 | }; | ||
2291 | |||
2292 | static 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 | |||
2311 | struct 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 | |||
2339 | static THREAD_FUNC_DECL CrcThreadFunction(void *param); | ||
2340 | |||
2341 | struct 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 | |||
2383 | static 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 | |||
2386 | void 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 | |||
2428 | static 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 | |||
2440 | struct 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 | |||
2459 | WRes 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 | |||
2481 | static 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 | /* | ||
2490 | static UInt32 RandGenCrc(Byte *buf, size_t size, CBaseRandomGenerator &RG) | ||
2491 | { | ||
2492 | RandGen(buf, size, RG); | ||
2493 | return CrcCalc1(buf, size); | ||
2494 | } | ||
2495 | */ | ||
2496 | |||
2497 | static 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 | |||
2521 | struct 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 | |||
2540 | static 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 | |||
2599 | struct 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 | |||
2610 | static 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 | |||
2628 | static 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 | |||
2651 | static const unsigned kFieldSize_Name = 12; | ||
2652 | static const unsigned kFieldSize_SmallName = 4; | ||
2653 | static const unsigned kFieldSize_Speed = 9; | ||
2654 | static const unsigned kFieldSize_Usage = 5; | ||
2655 | static const unsigned kFieldSize_RU = 6; | ||
2656 | static const unsigned kFieldSize_Rating = 6; | ||
2657 | static const unsigned kFieldSize_EU = 5; | ||
2658 | static const unsigned kFieldSize_Effec = 5; | ||
2659 | |||
2660 | static const unsigned kFieldSize_TotalSize = 4 + kFieldSize_Speed + kFieldSize_Usage + kFieldSize_RU + kFieldSize_Rating; | ||
2661 | static const unsigned kFieldSize_EUAndEffec = 2 + kFieldSize_EU + kFieldSize_Effec; | ||
2662 | |||
2663 | |||
2664 | static void PrintRating(IBenchPrintCallback &f, UInt64 rating, unsigned size) | ||
2665 | { | ||
2666 | PrintNumber(f, (rating + 500000) / 1000000, size); | ||
2667 | } | ||
2668 | |||
2669 | |||
2670 | static 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 | |||
2678 | static 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 | |||
2686 | static void PrintSpaces(IBenchPrintCallback &f, unsigned size) | ||
2687 | { | ||
2688 | PrintChars(f, ' ', size); | ||
2689 | } | ||
2690 | |||
2691 | static void PrintUsage(IBenchPrintCallback &f, UInt64 usage, unsigned size) | ||
2692 | { | ||
2693 | PrintNumber(f, Benchmark_GetUsage_Percents(usage), size); | ||
2694 | } | ||
2695 | |||
2696 | static 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 | |||
2714 | void CTotalBenchRes::Generate_From_BenchInfo(const CBenchInfo &info) | ||
2715 | { | ||
2716 | Speed = info.GetUnpackSizeSpeed(); | ||
2717 | Usage = info.GetUsage(); | ||
2718 | RPU = info.GetRatingPerUsage(Rating); | ||
2719 | } | ||
2720 | |||
2721 | void CTotalBenchRes::Mult_For_Weight(unsigned weight) | ||
2722 | { | ||
2723 | NumIterations2 *= weight; | ||
2724 | RPU *= weight; | ||
2725 | Rating *= weight; | ||
2726 | Usage *= weight; | ||
2727 | Speed *= weight; | ||
2728 | } | ||
2729 | |||
2730 | void 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 | |||
2740 | static 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 | |||
2772 | static 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 | |||
2788 | static void PrintHex(AString &s, UInt64 v) | ||
2789 | { | ||
2790 | char temp[32]; | ||
2791 | ConvertUInt64ToHex(v, temp); | ||
2792 | s += temp; | ||
2793 | } | ||
2794 | |||
2795 | AString 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 | ||
2836 | extern bool g_LargePagesMode; | ||
2837 | extern "C" | ||
2838 | { | ||
2839 | extern SIZE_T g_LargePageSize; | ||
2840 | } | ||
2841 | #endif | ||
2842 | |||
2843 | void 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 | |||
2867 | static 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 | |||
2893 | struct 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 | |||
2928 | HRESULT CBenchCallbackToPrint::SetFreq(bool showFreq, UInt64 cpuFreq) | ||
2929 | { | ||
2930 | ShowFreq = showFreq; | ||
2931 | CpuFreq = cpuFreq; | ||
2932 | return S_OK; | ||
2933 | } | ||
2934 | |||
2935 | HRESULT 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 | |||
2950 | static const char * const kSep = " | "; | ||
2951 | |||
2952 | HRESULT 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 | |||
2973 | void CBenchCallbackToPrint::Print(const char *s) | ||
2974 | { | ||
2975 | _file->Print(s); | ||
2976 | } | ||
2977 | |||
2978 | void CBenchCallbackToPrint::NewLine() | ||
2979 | { | ||
2980 | _file->NewLine(); | ||
2981 | } | ||
2982 | |||
2983 | static 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 | |||
2991 | static 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 | |||
2999 | static 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 | |||
3066 | struct 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 | |||
3090 | HRESULT 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 | |||
3204 | static 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 | |||
3396 | static 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 | |||
3451 | struct CTempValues | ||
3452 | { | ||
3453 | UInt64 *Values; | ||
3454 | CTempValues(UInt32 num) { Values = new UInt64[num]; } | ||
3455 | ~CTempValues() { delete []Values; } | ||
3456 | }; | ||
3457 | |||
3458 | static 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 | |||
3471 | static bool AreSameMethodNames(const char *fullName, const char *shortName) | ||
3472 | { | ||
3473 | return StringsAreEqualNoCase_Ascii(fullName, shortName); | ||
3474 | } | ||
3475 | |||
3476 | |||
3477 | |||
3478 | |||
3479 | static void Print_Usage_and_Threads(IBenchPrintCallback &f, UInt64 usage, UInt32 threads) | ||
3480 | { | ||
3481 | PrintRequirements(f, "usage:", true, usage, "Benchmark threads: ", threads); | ||
3482 | } | ||
3483 | |||
3484 | |||
3485 | HRESULT 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 | |||
11 | UInt64 Benchmark_GetUsage_Percents(UInt64 usage); | ||
12 | |||
13 | struct 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 | |||
46 | struct 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 | |||
75 | struct 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 | |||
84 | const unsigned kBenchMinDicLogSize = 18; | ||
85 | |||
86 | UInt64 GetBenchMemoryUsage(UInt32 numThreads, int level, UInt64 dictionary, bool totalBench); | ||
87 | |||
88 | struct IBenchPrintCallback | ||
89 | { | ||
90 | virtual void Print(const char *s) = 0; | ||
91 | virtual void NewLine() = 0; | ||
92 | virtual HRESULT CheckBreak() = 0; | ||
93 | }; | ||
94 | |||
95 | struct IBenchFreqCallback | ||
96 | { | ||
97 | virtual HRESULT AddCpuFreq(unsigned numThreads, UInt64 freq, UInt64 usage) = 0; | ||
98 | virtual HRESULT FreqsFinished(unsigned numThreads) = 0; | ||
99 | }; | ||
100 | |||
101 | HRESULT 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 | |||
110 | AString GetProcessThreadsInfo(const NWindows::NSystem::CProcessAffinity &ti); | ||
111 | |||
112 | void GetSysInfo(AString &s1, AString &s2); | ||
113 | void GetCpuName(AString &s); | ||
114 | void AddCpuFeatures(AString &s); | ||
115 | |||
116 | #ifdef _7ZIP_LARGE_PAGES | ||
117 | void 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 | |||
24 | using 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 | |||
48 | extern HWND g_HWND; | ||
49 | |||
50 | UString GetQuotedString(const UString &s) | ||
51 | { | ||
52 | UString s2 ('\"'); | ||
53 | s2 += s; | ||
54 | s2 += '\"'; | ||
55 | return s2; | ||
56 | } | ||
57 | |||
58 | static void ErrorMessage(LPCWSTR message) | ||
59 | { | ||
60 | MessageBoxW(g_HWND, message, L"7-Zip", MB_ICONERROR | MB_OK); | ||
61 | } | ||
62 | |||
63 | static 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 | |||
74 | static HRESULT Call7zGui(const UString ¶ms, | ||
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 | |||
100 | static void AddLagePagesSwitch(UString ¶ms) | ||
101 | { | ||
102 | if (ReadLockMemoryEnable()) | ||
103 | #ifndef UNDER_CE | ||
104 | if (NSecurity::Get_LargePages_RiskLevel() == 0) | ||
105 | #endif | ||
106 | params += " -slp"; | ||
107 | } | ||
108 | |||
109 | class CRandNameGenerator | ||
110 | { | ||
111 | CRandom _random; | ||
112 | public: | ||
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 | |||
121 | static HRESULT CreateMap(const UStringVector &names, | ||
122 | CFileMapping &fileMapping, NSynchronization::CManualResetEvent &event, | ||
123 | UString ¶ms) | ||
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 | |||
186 | HRESULT 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 | |||
242 | static void ExtractGroupCommand(const UStringVector &arcPaths, UString ¶ms, 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 | |||
255 | void 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 | |||
273 | void 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 | |||
287 | void 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 | |||
327 | void 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 | |||
8 | UString GetQuotedString(const UString &s); | ||
9 | |||
10 | HRESULT 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 | |||
18 | void ExtractArchives(const UStringVector &arcPaths, const UString &outFolder, bool showDialog, bool elimDup); | ||
19 | void TestArchives(const UStringVector &arcPaths, bool hashMode = false); | ||
20 | |||
21 | void CalcChecksum(const UStringVector &paths, | ||
22 | const UString &methodName, | ||
23 | const UString &arcPathPrefix, | ||
24 | const UString &arcFileName); | ||
25 | |||
26 | void 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 | |||
20 | extern 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 | |||
30 | static 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 | |||
65 | UString GetQuotedString(const UString &s) | ||
66 | { | ||
67 | UString s2 ('\"'); | ||
68 | s2 += s; | ||
69 | s2 += '\"'; | ||
70 | return s2; | ||
71 | } | ||
72 | |||
73 | static void ErrorMessage(LPCWSTR message) | ||
74 | { | ||
75 | MessageBoxW(g_HWND, message, L"7-Zip", MB_ICONERROR); | ||
76 | } | ||
77 | |||
78 | static void ErrorMessageHRESULT(HRESULT res) | ||
79 | { | ||
80 | ErrorMessage(HResultToMessage(res)); | ||
81 | } | ||
82 | |||
83 | static void ErrorLangMessage(UINT resourceID) | ||
84 | { | ||
85 | ErrorMessage(LangString(resourceID)); | ||
86 | } | ||
87 | |||
88 | HRESULT 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 | |||
154 | static 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 | |||
231 | void ExtractArchives(const UStringVector &arcPaths, const UString &outFolder, bool showDialog, bool elimDup) | ||
232 | { | ||
233 | ExtractGroupCommand(arcPaths, showDialog, outFolder, false, elimDup); | ||
234 | } | ||
235 | |||
236 | void TestArchives(const UStringVector &arcPaths, bool hashMode) | ||
237 | { | ||
238 | ExtractGroupCommand(arcPaths, true, UString(), true, | ||
239 | false, // elimDup | ||
240 | hashMode ? "hash" : NULL); | ||
241 | } | ||
242 | |||
243 | void 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 | |||
297 | void 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 | |||
7 | static 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 | |||
31 | UString 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 | |||
8 | UString 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 | |||
18 | struct 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 | |||
51 | struct 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 | |||
78 | struct IDirItemsCallback | ||
79 | { | ||
80 | INTERFACE_IDirItemsCallback(=0) | ||
81 | }; | ||
82 | |||
83 | struct 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 | |||
131 | class 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 | |||
141 | public: | ||
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 | |||
207 | struct 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 | |||
22 | using namespace NWindows; | ||
23 | using namespace NFile; | ||
24 | using namespace NName; | ||
25 | |||
26 | void 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 | |||
62 | HRESULT 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 | |||
72 | HRESULT CDirItems::AddError(const FString &path) | ||
73 | { | ||
74 | return AddError(path, ::GetLastError()); | ||
75 | } | ||
76 | |||
77 | static const unsigned kScanProgressStepMask = (1 << 12) - 1; | ||
78 | |||
79 | HRESULT CDirItems::ScanProgress(const FString &dirPath) | ||
80 | { | ||
81 | if (Callback) | ||
82 | return Callback->ScanProgress(Stat, dirPath, true); | ||
83 | return S_OK; | ||
84 | } | ||
85 | |||
86 | UString 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 | |||
110 | FString CDirItems::GetPhyPath(unsigned index) const | ||
111 | { | ||
112 | const CDirItem &di = Items[index]; | ||
113 | return us2fs(GetPrefixesPath(PhyParents, di.PhyParent, di.Name)); | ||
114 | } | ||
115 | |||
116 | UString CDirItems::GetLogPath(unsigned index) const | ||
117 | { | ||
118 | const CDirItem &di = Items[index]; | ||
119 | return GetPrefixesPath(LogParents, di.LogParent, di.Name); | ||
120 | } | ||
121 | |||
122 | void CDirItems::ReserveDown() | ||
123 | { | ||
124 | Prefixes.ReserveDown(); | ||
125 | PhyParents.ReserveDown(); | ||
126 | LogParents.ReserveDown(); | ||
127 | Items.ReserveDown(); | ||
128 | } | ||
129 | |||
130 | unsigned 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 | |||
137 | void CDirItems::DeleteLastPrefix() | ||
138 | { | ||
139 | PhyParents.DeleteBack(); | ||
140 | LogParents.DeleteBack(); | ||
141 | Prefixes.DeleteBack(); | ||
142 | } | ||
143 | |||
144 | bool InitLocalPrivileges(); | ||
145 | |||
146 | CDirItems::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 | |||
164 | HRESULT 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 | |||
221 | HRESULT 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 | |||
295 | HRESULT 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 | /* | ||
352 | EnumerateItems2() | ||
353 | const FStringVector &filePaths - are path without tail slashes. | ||
354 | All dir prefixes of filePaths will be not stores in logical paths | ||
355 | fix 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 | |||
368 | HRESULT 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 | |||
426 | static 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 | |||
446 | static 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 | |||
470 | static 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 | |||
521 | HRESULT 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 | |||
552 | static 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 | |||
692 | static 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 | |||
724 | static 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 | |||
737 | static 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 | |||
1112 | HRESULT 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 | |||
1150 | HRESULT 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 | |||
1285 | static const char * const kCannotFindArchive = "Cannot find archive"; | ||
1286 | |||
1287 | HRESULT 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 | |||
1356 | static 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 | |||
1370 | static 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 | |||
1383 | static 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 | |||
1419 | void 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 | |||
1431 | CMessagePathException::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 | |||
1441 | CMessagePathException::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 | |||
11 | HRESULT EnumerateItems( | ||
12 | const NWildcard::CCensor &censor, | ||
13 | NWildcard::ECensorPathMode pathMode, | ||
14 | const UString &addPathPrefix, | ||
15 | CDirItems &dirItems); | ||
16 | |||
17 | |||
18 | struct 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 | |||
25 | HRESULT 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 | ||
35 | void 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 | |||
6 | namespace NExitCode { | ||
7 | |||
8 | enum 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 | |||
21 | using namespace NWindows; | ||
22 | using namespace NFile; | ||
23 | using namespace NDir; | ||
24 | |||
25 | |||
26 | static 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 | |||
38 | static 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 | |||
242 | int Find_FileName_InSortedVector(const UStringVector &fileName, const UString &name); | ||
243 | int 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 | |||
263 | HRESULT 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 | |||
17 | struct 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 | |||
43 | struct 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 | |||
71 | struct 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 | |||
87 | HRESULT 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 | |||
6 | namespace NExtract { | ||
7 | |||
8 | namespace 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 | |||
20 | namespace 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 | |||
11 | extern | ||
12 | bool g_PathTrailReplaceMode; | ||
13 | bool g_PathTrailReplaceMode = | ||
14 | #ifdef _WIN32 | ||
15 | true | ||
16 | #else | ||
17 | false | ||
18 | #endif | ||
19 | ; | ||
20 | |||
21 | |||
22 | #ifdef _WIN32 | ||
23 | static 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 | |||
87 | void 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 | |||
108 | static const unsigned g_ReservedWithNum_Index = 4; | ||
109 | |||
110 | static const char * const g_ReservedNames[] = | ||
111 | { | ||
112 | "CON", "PRN", "AUX", "NUL", | ||
113 | "COM", "LPT" | ||
114 | }; | ||
115 | |||
116 | static 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 | |||
145 | static void CorrectUnsupportedName(UString &name) | ||
146 | { | ||
147 | if (!IsSupportedName(name)) | ||
148 | name.InsertAtFront(L'_'); | ||
149 | } | ||
150 | |||
151 | #endif | ||
152 | |||
153 | static 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 = "[]"; | ||
168 | static const char k_EmptyReplaceName = '_'; | ||
169 | |||
170 | UString 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 | |||
185 | void 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 | |||
277 | UString 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 | ||
9 | void Correct_AltStream_Name(UString &s); | ||
10 | // #endif | ||
11 | |||
12 | // replaces unsuported characters, and replaces "." , ".." and "" to "[]" | ||
13 | UString 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 | */ | ||
27 | void Correct_FsPath(bool absIsAllowed, bool keepAndReplaceEmptyPrefixes, UStringVector &parts, bool isDir); | ||
28 | |||
29 | UString 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 | |||
22 | using namespace NWindows; | ||
23 | |||
24 | #ifdef EXTERNAL_CODECS | ||
25 | extern const CExternalCodecs *g_ExternalCodecs_Ptr; | ||
26 | #endif | ||
27 | |||
28 | class CHashMidBuf | ||
29 | { | ||
30 | void *_data; | ||
31 | public: | ||
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 | |||
44 | static const char * const k_DefaultHashMethod = "CRC32"; | ||
45 | |||
46 | HRESULT 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 | |||
118 | void 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 | |||
129 | void 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 | |||
136 | void CHashBundle::SetSize(UInt64 size) | ||
137 | { | ||
138 | CurSize = size; | ||
139 | } | ||
140 | |||
141 | static 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 | |||
178 | void CHasherState::AddDigest(unsigned groupIndex, const Byte *data) | ||
179 | { | ||
180 | NumSums[groupIndex]++; | ||
181 | AddDigests(Digests[groupIndex], data, DigestSize); | ||
182 | } | ||
183 | |||
184 | void 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 | |||
243 | static 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 | |||
261 | static 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 | |||
296 | static void SetSpacesAndNul(char *s, unsigned num) | ||
297 | { | ||
298 | for (unsigned i = 0; i < num; i++) | ||
299 | s[i] = ' '; | ||
300 | s[num] = 0; | ||
301 | } | ||
302 | |||
303 | static const unsigned kHashColumnWidth_Min = 4 * 2; | ||
304 | |||
305 | static unsigned GetColumnWidth(unsigned digestSize) | ||
306 | { | ||
307 | const unsigned width = digestSize * 2; | ||
308 | return width < kHashColumnWidth_Min ? kHashColumnWidth_Min: width; | ||
309 | } | ||
310 | |||
311 | |||
312 | void HashHexToString(char *dest, const Byte *data, UInt32 size); | ||
313 | |||
314 | static 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 | |||
357 | static void Add_LF(CDynLimBuf &hashFileString, const CHashOptionsLocal &options) | ||
358 | { | ||
359 | hashFileString += (char)(options.HashMode_Zero.Val ? 0 : '\n'); | ||
360 | } | ||
361 | |||
362 | |||
363 | |||
364 | |||
365 | static 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 | |||
436 | static 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 | |||
452 | HRESULT 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 | |||
631 | static inline char GetHex_Upper(unsigned v) | ||
632 | { | ||
633 | return (char)((v < 10) ? ('0' + v) : ('A' + (v - 10))); | ||
634 | } | ||
635 | |||
636 | static inline char GetHex_Lower(unsigned v) | ||
637 | { | ||
638 | return (char)((v < 10) ? ('0' + v) : ('a' + (v - 10))); | ||
639 | } | ||
640 | |||
641 | void 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 | |||
679 | void 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 | |||
704 | namespace NHash { | ||
705 | |||
706 | static 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 | |||
733 | bool 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 | |||
745 | bool 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 | |||
772 | static const char *SkipWhite(const char *s) | ||
773 | { | ||
774 | while (IsWhite(*s)) | ||
775 | s++; | ||
776 | return s; | ||
777 | } | ||
778 | |||
779 | static 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 | |||
795 | static 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 | |||
828 | bool 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 | |||
931 | static 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 | |||
975 | static 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 | |||
999 | static const Byte kArcProps[] = | ||
1000 | { | ||
1001 | // kpidComment, | ||
1002 | kpidCharacts | ||
1003 | }; | ||
1004 | |||
1005 | static const Byte kProps[] = | ||
1006 | { | ||
1007 | kpidPath, | ||
1008 | kpidSize, | ||
1009 | kpidPackSize, | ||
1010 | kpidMethod | ||
1011 | }; | ||
1012 | |||
1013 | static const Byte kRawProps[] = | ||
1014 | { | ||
1015 | kpidChecksum | ||
1016 | }; | ||
1017 | |||
1018 | |||
1019 | STDMETHODIMP CHandler::GetParent(UInt32 /* index */ , UInt32 *parent, UInt32 *parentType) | ||
1020 | { | ||
1021 | *parentType = NParentType::kDir; | ||
1022 | *parent = (UInt32)(Int32)-1; | ||
1023 | return S_OK; | ||
1024 | } | ||
1025 | |||
1026 | STDMETHODIMP CHandler::GetNumRawProps(UInt32 *numProps) | ||
1027 | { | ||
1028 | *numProps = ARRAY_SIZE(kRawProps); | ||
1029 | return S_OK; | ||
1030 | } | ||
1031 | |||
1032 | STDMETHODIMP CHandler::GetRawPropInfo(UInt32 index, BSTR *name, PROPID *propID) | ||
1033 | { | ||
1034 | *propID = kRawProps[index]; | ||
1035 | *name = 0; | ||
1036 | return S_OK; | ||
1037 | } | ||
1038 | |||
1039 | STDMETHODIMP 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 | |||
1060 | IMP_IInArchive_Props | ||
1061 | IMP_IInArchive_ArcProps | ||
1062 | |||
1063 | STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) | ||
1064 | { | ||
1065 | *numItems = HashPairs.Size(); | ||
1066 | return S_OK; | ||
1067 | } | ||
1068 | |||
1069 | static void Add_OptSpace_String(UString &dest, const char *src) | ||
1070 | { | ||
1071 | dest.Add_Space_if_NotEmpty(); | ||
1072 | dest += src; | ||
1073 | } | ||
1074 | |||
1075 | STDMETHODIMP 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 | |||
1138 | STDMETHODIMP 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 | |||
1188 | static 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 | |||
1220 | STDMETHODIMP 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 | |||
1348 | void 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 | |||
1362 | STDMETHODIMP CHandler::Close() | ||
1363 | { | ||
1364 | ClearVars(); | ||
1365 | _nameExtenstion.Empty(); | ||
1366 | _pgpMethod.Empty(); | ||
1367 | HashPairs.Clear(); | ||
1368 | return S_OK; | ||
1369 | } | ||
1370 | |||
1371 | |||
1372 | static 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 | |||
1392 | static 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 | |||
1412 | STDMETHODIMP 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 | |||
1635 | struct 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 | |||
1649 | static 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 | |||
1667 | STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type) | ||
1668 | { | ||
1669 | *type = NFileTimeType::kUnix; | ||
1670 | return S_OK; | ||
1671 | } | ||
1672 | |||
1673 | |||
1674 | STDMETHODIMP 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 | |||
1937 | HRESULT 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 | |||
1982 | STDMETHODIMP 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 | |||
1996 | CHandler::CHandler() | ||
1997 | { | ||
1998 | ClearVars(); | ||
1999 | InitProps(); | ||
2000 | } | ||
2001 | |||
2002 | } | ||
2003 | |||
2004 | |||
2005 | |||
2006 | static IInArchive *CreateHashHandler_In() { return new NHash::CHandler; } | ||
2007 | static IOutArchive *CreateHashHandler_Out() { return new NHash::CHandler; } | ||
2008 | |||
2009 | void 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 | |||
15 | const unsigned k_HashCalc_DigestSize_Max = 64; | ||
16 | const unsigned k_HashCalc_ExtraSize = 8; | ||
17 | const unsigned k_HashCalc_NumGroups = 4; | ||
18 | |||
19 | enum | ||
20 | { | ||
21 | k_HashCalc_Index_Current, | ||
22 | k_HashCalc_Index_DataSum, | ||
23 | k_HashCalc_Index_NamesSum, | ||
24 | k_HashCalc_Index_StreamsSum | ||
25 | }; | ||
26 | |||
27 | struct 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 | |||
63 | struct 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 | |||
71 | struct 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 | |||
117 | struct IHashCallbackUI: public IDirItemsCallback | ||
118 | { | ||
119 | INTERFACE_IHashCallbackUI(=0) | ||
120 | }; | ||
121 | |||
122 | |||
123 | struct 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 | |||
178 | struct 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 | |||
201 | HRESULT 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 | |||
212 | namespace NHash { | ||
213 | |||
214 | struct 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 | |||
261 | class 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 | |||
309 | public: | ||
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 | |||
329 | void 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 | |||
13 | namespace NOverwriteAnswer | ||
14 | { | ||
15 | enum EEnum | ||
16 | { | ||
17 | kYes, | ||
18 | kYesToAll, | ||
19 | kNo, | ||
20 | kNoToAll, | ||
21 | kAutoRename, | ||
22 | kCancel | ||
23 | }; | ||
24 | } | ||
25 | |||
26 | |||
27 | /* ---------- IFolderArchiveExtractCallback ---------- | ||
28 | is implemented by | ||
29 | Console/ExtractCallbackConsole.h CExtractCallbackConsole | ||
30 | FileManager/ExtractCallback.h CExtractCallbackImp | ||
31 | FAR/ExtractEngine.cpp CExtractCallBackImp: (QueryInterface is not supported) | ||
32 | |||
33 | IID_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 | |||
54 | DECL_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 | |||
62 | DECL_INTERFACE_SUB(IFolderArchiveExtractCallback2, IUnknown, 0x01, 0x08) | ||
63 | { | ||
64 | INTERFACE_IFolderArchiveExtractCallback2(PURE) | ||
65 | }; | ||
66 | |||
67 | /* ---------- IExtractCallbackUI ---------- | ||
68 | is 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 | |||
87 | struct 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 | |||
97 | DECL_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 | |||
108 | DECL_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 | /* | ||
4 | EXTERNAL_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 | |||
23 | EXPORT_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 | |||
48 | using 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 | ||
65 | extern HINSTANCE g_hInstance; | ||
66 | #include "../../../Windows/ResourceString.h" | ||
67 | static 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 | |||
80 | using namespace NFile; | ||
81 | |||
82 | |||
83 | #define kCodecsFolderName FTEXT("Codecs") | ||
84 | #define kFormatsFolderName FTEXT("Formats") | ||
85 | |||
86 | |||
87 | static CFSTR const kMainDll = | ||
88 | #ifdef _WIN32 | ||
89 | FTEXT("7z.dll"); | ||
90 | #else | ||
91 | FTEXT("7z.so"); | ||
92 | #endif | ||
93 | |||
94 | |||
95 | #ifdef _WIN32 | ||
96 | |||
97 | static LPCTSTR const kRegistryPath = TEXT("Software") TEXT(STRING_PATH_SEPARATOR) TEXT("7-zip"); | ||
98 | static LPCWSTR const kProgramPathValue = L"Path"; | ||
99 | static LPCWSTR const kProgramPath2Value = L"Path" | ||
100 | #ifdef _WIN64 | ||
101 | L"64"; | ||
102 | #else | ||
103 | L"32"; | ||
104 | #endif | ||
105 | |||
106 | static 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 | |||
127 | static const unsigned kNumArcsMax = 64; | ||
128 | static unsigned g_NumArcs = 0; | ||
129 | static const CArcInfo *g_Arcs[kNumArcsMax]; | ||
130 | |||
131 | void RegisterArc(const CArcInfo *arcInfo) throw() | ||
132 | { | ||
133 | if (g_NumArcs < kNumArcsMax) | ||
134 | { | ||
135 | g_Arcs[g_NumArcs] = arcInfo; | ||
136 | g_NumArcs++; | ||
137 | } | ||
138 | } | ||
139 | |||
140 | static 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 | |||
165 | int 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 | |||
173 | void 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 | |||
194 | static 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 | |||
216 | static 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 | |||
238 | static 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 | |||
257 | static 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 | |||
278 | HRESULT 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 | |||
328 | static 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 | |||
338 | static 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 | |||
353 | static 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 | |||
372 | static 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 | |||
387 | static 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 | |||
405 | static const UInt32 kArcFlagsPars[] = | ||
406 | { | ||
407 | NArchive::NHandlerPropID::kKeepName, NArcInfoFlags::kKeepName, | ||
408 | NArchive::NHandlerPropID::kAltStreams, NArcInfoFlags::kAltStreams, | ||
409 | NArchive::NHandlerPropID::kNtSecure, NArcInfoFlags::kNtSecure | ||
410 | }; | ||
411 | |||
412 | HRESULT 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 | ||
506 | extern "C" | ||
507 | { | ||
508 | extern SIZE_T g_LargePageSize; | ||
509 | } | ||
510 | #endif | ||
511 | |||
512 | |||
513 | void 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 | |||
521 | HRESULT 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 | |||
644 | HRESULT 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 | |||
684 | void 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 | |||
710 | HRESULT 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 | |||
816 | int 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 | |||
839 | int 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 | |||
849 | int 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 | |||
857 | bool 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 | |||
885 | void 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 | |||
915 | bool 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 | |||
939 | extern unsigned g_NumCodecs; | ||
940 | STDAPI CreateDecoder(UInt32 index, const GUID *iid, void **outObject); | ||
941 | STDAPI CreateEncoder(UInt32 index, const GUID *iid, void **outObject); | ||
942 | STDAPI GetMethodProperty(UInt32 codecIndex, PROPID propID, PROPVARIANT *value); | ||
943 | #define NUM_EXPORT_CODECS g_NumCodecs | ||
944 | |||
945 | extern unsigned g_NumHashers; | ||
946 | STDAPI CreateHasher(UInt32 index, IHasher **hasher); | ||
947 | STDAPI 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 | |||
957 | STDMETHODIMP 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 | |||
967 | STDMETHODIMP 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 | |||
1003 | STDMETHODIMP 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 | |||
1026 | STDMETHODIMP 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 | |||
1050 | STDMETHODIMP_(UInt32) CCodecs::GetNumHashers() | ||
1051 | { | ||
1052 | return NUM_EXPORT_HASHERS | ||
1053 | #ifdef EXTERNAL_CODECS | ||
1054 | + Hashers.Size() | ||
1055 | #endif | ||
1056 | ; | ||
1057 | } | ||
1058 | |||
1059 | STDMETHODIMP 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 | |||
1074 | STDMETHODIMP 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 | |||
1088 | int 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 | |||
1103 | int 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 | |||
1118 | bool 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 | |||
1141 | bool 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 | |||
1164 | bool 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 | |||
1195 | UInt32 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 | |||
1207 | HRESULT 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 | |||
1217 | AString 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 | |||
1227 | UInt64 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 | |||
1237 | AString 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 | |||
1247 | UInt32 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 | |||
1257 | void 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 | |||
1283 | extern unsigned g_NumCodecs; | ||
1284 | extern const CCodecInfo *g_Codecs[]; | ||
1285 | |||
1286 | void 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 | /* | ||
7 | Client application uses LoadCodecs.* to load plugins to | ||
8 | CCodecs 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 | |||
13 | EXTERNAL_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 | |||
65 | struct 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 | |||
77 | struct CDllHasherInfo | ||
78 | { | ||
79 | unsigned LibIndex; | ||
80 | UInt32 HasherIndex; | ||
81 | }; | ||
82 | |||
83 | #endif | ||
84 | |||
85 | struct 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 | |||
96 | struct 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 | |||
203 | struct 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 | |||
220 | struct 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 | |||
251 | struct CCodecError | ||
252 | { | ||
253 | FString Path; | ||
254 | HRESULT ErrorCode; | ||
255 | AString Message; | ||
256 | CCodecError(): ErrorCode(0) {} | ||
257 | }; | ||
258 | |||
259 | |||
260 | struct 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 | |||
274 | class 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); | ||
284 | public: | ||
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 | ||
45 | static const UInt64 kMaxCheckStartPosition = 1 << 23; | ||
46 | |||
47 | /* | ||
48 | Open: | ||
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 | |||
95 | using 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 | /* | ||
106 | CArc::~CArc() | ||
107 | { | ||
108 | GetRawProps.Release(); | ||
109 | Archive.Release(); | ||
110 | printf("\nCArc::~CArc()\n"); | ||
111 | } | ||
112 | */ | ||
113 | |||
114 | #ifndef _SFX | ||
115 | |||
116 | namespace NArchive { | ||
117 | namespace NParser { | ||
118 | |||
119 | struct 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 | |||
175 | class CHandler: | ||
176 | public IInArchive, | ||
177 | public IInArchiveGetStream, | ||
178 | public CMyUnknownImp | ||
179 | { | ||
180 | public: | ||
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 | |||
207 | int 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 | |||
233 | void 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 | |||
262 | void 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 | /* | ||
276 | static 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 | |||
289 | static const Byte kProps[] = | ||
290 | { | ||
291 | kpidPath, | ||
292 | kpidSize, | ||
293 | kpidMTime, | ||
294 | kpidType, | ||
295 | kpidComment, | ||
296 | kpidOffset, | ||
297 | kpidUnpackSize | ||
298 | }; | ||
299 | |||
300 | IMP_IInArchive_Props | ||
301 | IMP_IInArchive_ArcProps_NO | ||
302 | |||
303 | STDMETHODIMP 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 | |||
314 | STDMETHODIMP CHandler::Close() | ||
315 | { | ||
316 | _items.Clear(); | ||
317 | _stream.Release(); | ||
318 | return S_OK; | ||
319 | } | ||
320 | |||
321 | STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) | ||
322 | { | ||
323 | *numItems = _items.Size(); | ||
324 | return S_OK; | ||
325 | } | ||
326 | |||
327 | STDMETHODIMP 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 | |||
368 | HRESULT 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 | |||
441 | STDMETHODIMP 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 | |||
453 | HRESULT 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 | |||
465 | HRESULT Archive_IsItem_Dir(IInArchive *arc, UInt32 index, bool &result) throw() | ||
466 | { | ||
467 | return Archive_GetItemBoolProp(arc, index, kpidIsDir, result); | ||
468 | } | ||
469 | |||
470 | HRESULT Archive_IsItem_Aux(IInArchive *arc, UInt32 index, bool &result) throw() | ||
471 | { | ||
472 | return Archive_GetItemBoolProp(arc, index, kpidIsAux, result); | ||
473 | } | ||
474 | |||
475 | HRESULT Archive_IsItem_AltStream(IInArchive *arc, UInt32 index, bool &result) throw() | ||
476 | { | ||
477 | return Archive_GetItemBoolProp(arc, index, kpidIsAltStream, result); | ||
478 | } | ||
479 | |||
480 | HRESULT Archive_IsItem_Deleted(IInArchive *arc, UInt32 index, bool &result) throw() | ||
481 | { | ||
482 | return Archive_GetItemBoolProp(arc, index, kpidIsDeleted, result); | ||
483 | } | ||
484 | |||
485 | static 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 | |||
497 | static 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 | |||
515 | static 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 | |||
535 | HRESULT 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 | |||
619 | HRESULT 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 | |||
761 | HRESULT 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 | |||
782 | HRESULT 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 | |||
797 | int 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 | |||
819 | HRESULT 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 | |||
952 | static 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 | |||
973 | HRESULT 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 | |||
992 | HRESULT 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 | |||
1015 | static 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 | |||
1023 | static 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 | |||
1068 | static bool IsExeExt(const UString &ext) | ||
1069 | { | ||
1070 | return ext.IsEqualTo_Ascii_NoCase("exe"); | ||
1071 | } | ||
1072 | |||
1073 | static const char * const k_PreArcFormats[] = | ||
1074 | { | ||
1075 | "pe" | ||
1076 | , "elf" | ||
1077 | , "macho" | ||
1078 | , "mub" | ||
1079 | , "te" | ||
1080 | }; | ||
1081 | |||
1082 | static 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 | |||
1091 | static 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 | |||
1098 | static 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 | |||
1112 | static 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 | |||
1120 | class CArchiveOpenCallback_Offset: | ||
1121 | public IArchiveOpenCallback, | ||
1122 | public IArchiveOpenVolumeCallback, | ||
1123 | #ifndef _NO_CRYPTO | ||
1124 | public ICryptoGetTextPassword, | ||
1125 | #endif | ||
1126 | public CMyUnknownImp | ||
1127 | { | ||
1128 | public: | ||
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 | ||
1154 | STDMETHODIMP 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 | |||
1164 | STDMETHODIMP CArchiveOpenCallback_Offset::SetTotal(const UInt64 *, const UInt64 *) | ||
1165 | { | ||
1166 | return S_OK; | ||
1167 | } | ||
1168 | |||
1169 | STDMETHODIMP 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 | |||
1179 | STDMETHODIMP 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 | |||
1188 | STDMETHODIMP 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 | |||
1198 | UInt32 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 | |||
1212 | void 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 | |||
1229 | HRESULT 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 | /* | ||
1298 | static 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 | |||
1307 | HRESULT 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 | |||
1375 | static 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 | |||
1469 | HRESULT 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 | |||
1503 | class CExtractCallback_To_OpenCallback: | ||
1504 | public IArchiveExtractCallback, | ||
1505 | public ICompressProgressInfo, | ||
1506 | public CMyUnknownImp | ||
1507 | { | ||
1508 | public: | ||
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 | |||
1524 | STDMETHODIMP CExtractCallback_To_OpenCallback::SetTotal(UInt64 /* size */) | ||
1525 | { | ||
1526 | return S_OK; | ||
1527 | } | ||
1528 | |||
1529 | STDMETHODIMP CExtractCallback_To_OpenCallback::SetCompleted(const UInt64 * /* completeValue */) | ||
1530 | { | ||
1531 | return S_OK; | ||
1532 | } | ||
1533 | |||
1534 | STDMETHODIMP 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 | |||
1546 | STDMETHODIMP CExtractCallback_To_OpenCallback::GetStream(UInt32 /* index */, ISequentialOutStream **outStream, Int32 /* askExtractMode */) | ||
1547 | { | ||
1548 | *outStream = NULL; | ||
1549 | return S_OK; | ||
1550 | } | ||
1551 | |||
1552 | STDMETHODIMP CExtractCallback_To_OpenCallback::PrepareOperation(Int32 /* askExtractMode */) | ||
1553 | { | ||
1554 | return S_OK; | ||
1555 | } | ||
1556 | |||
1557 | STDMETHODIMP CExtractCallback_To_OpenCallback::SetOperationResult(Int32 /* operationResult */) | ||
1558 | { | ||
1559 | return S_OK; | ||
1560 | } | ||
1561 | |||
1562 | |||
1563 | static 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 | |||
1603 | static 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 | |||
1617 | HRESULT 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 | |||
2984 | HRESULT 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 | |||
3043 | HRESULT 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 | |||
3127 | void 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 | |||
3139 | HRESULT 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 | |||
3151 | void CArchiveLink::Release() | ||
3152 | { | ||
3153 | // NonOpenErrorFormatIndex = -1; | ||
3154 | NonOpen_ErrorInfo.ClearErrors(); | ||
3155 | NonOpen_ArcPath.Empty(); | ||
3156 | while (!Arcs.IsEmpty()) | ||
3157 | Arcs.DeleteBack(); | ||
3158 | } | ||
3159 | |||
3160 | /* | ||
3161 | void 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 | |||
3189 | HRESULT 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 = ! | ||
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 | |||
3353 | HRESULT 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 | |||
3395 | HRESULT 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 | |||
3440 | HRESULT 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 | |||
3450 | HRESULT 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 = ! | ||
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 | |||
3495 | bool ParseComplexSize(const wchar_t *s, UInt64 &result); | ||
3496 | bool 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 | |||
3526 | static 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 | |||
3553 | static 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 | |||
3617 | bool 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 | /* | ||
3643 | bool 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 | |||
18 | HRESULT Archive_GetItemBoolProp(IInArchive *arc, UInt32 index, PROPID propID, bool &result) throw(); | ||
19 | HRESULT Archive_IsItem_Dir(IInArchive *arc, UInt32 index, bool &result) throw(); | ||
20 | HRESULT Archive_IsItem_Aux(IInArchive *arc, UInt32 index, bool &result) throw(); | ||
21 | HRESULT Archive_IsItem_AltStream(IInArchive *arc, UInt32 index, bool &result) throw(); | ||
22 | HRESULT Archive_IsItem_Deleted(IInArchive *arc, UInt32 index, bool &deleted) throw(); | ||
23 | |||
24 | #ifdef SUPPORT_ALT_STREAMS | ||
25 | int FindAltStreamColon_in_Path(const wchar_t *path); | ||
26 | #endif | ||
27 | |||
28 | /* | ||
29 | struct 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 | |||
43 | struct 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 | |||
60 | struct 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 | |||
116 | struct 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 | |||
146 | UInt32 GetOpenArcErrorFlags(const NWindows::NCOM::CPropVariant &prop, bool *isDefinedProp = NULL); | ||
147 | |||
148 | struct 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 | |||
227 | struct 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 | |||
263 | class 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 | |||
274 | public: | ||
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 | |||
375 | struct 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 | |||
431 | bool ParseOpenTypes(CCodecs &codecs, const UString &s, CObjectVector<COpenType> &types); | ||
432 | |||
433 | // bool IsHashType(const CObjectVector<COpenType> &types); | ||
434 | |||
435 | |||
436 | struct 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 | |||
22 | using namespace NWindows; | ||
23 | |||
24 | static const unsigned kNumWinAtrribFlags = 21; | ||
25 | static const char g_WinAttribChars[kNumWinAtrribFlags + 1] = "RHS8DAdNTsLCOIEV.X.PU"; | ||
26 | |||
27 | /* | ||
28 | FILE_ATTRIBUTE_ | ||
29 | |||
30 | 0 READONLY | ||
31 | 1 HIDDEN | ||
32 | 2 SYSTEM | ||
33 | 3 (Volume label - obsolete) | ||
34 | 4 DIRECTORY | ||
35 | 5 ARCHIVE | ||
36 | 6 DEVICE | ||
37 | 7 NORMAL | ||
38 | 8 TEMPORARY | ||
39 | 9 SPARSE_FILE | ||
40 | 10 REPARSE_POINT | ||
41 | 11 COMPRESSED | ||
42 | 12 OFFLINE | ||
43 | 13 NOT_CONTENT_INDEXED (I - Win10 attrib/Explorer) | ||
44 | 14 ENCRYPTED | ||
45 | 15 INTEGRITY_STREAM (V - ReFS Win8/Win2012) | ||
46 | 16 VIRTUAL (reserved) | ||
47 | 17 NO_SCRUB_DATA (X - ReFS Win8/Win2012 attrib) | ||
48 | 18 RECALL_ON_OPEN or EA | ||
49 | 19 PINNED | ||
50 | 20 UNPINNED | ||
51 | 21 STRICTLY_SEQUENTIAL | ||
52 | 22 RECALL_ON_DATA_ACCESS | ||
53 | */ | ||
54 | |||
55 | |||
56 | static 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 | |||
59 | static 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 | |||
82 | void 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 | |||
132 | void 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 | |||
206 | void 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 | |||
220 | static inline unsigned GetHex(unsigned v) | ||
221 | { | ||
222 | return (v < 10) ? ('0' + v) : ('A' + (v - 10)); | ||
223 | } | ||
224 | |||
225 | static inline void AddHexToString(AString &res, unsigned v) | ||
226 | { | ||
227 | res += (char)GetHex(v >> 4); | ||
228 | res += (char)GetHex(v & 0xF); | ||
229 | } | ||
230 | |||
231 | /* | ||
232 | static 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 | |||
241 | static 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 | |||
267 | struct CSecID2Name | ||
268 | { | ||
269 | UInt32 n; | ||
270 | const char *sz; | ||
271 | }; | ||
272 | |||
273 | static 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 | |||
281 | static 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 | |||
307 | static 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 | |||
326 | struct CServicesToName | ||
327 | { | ||
328 | UInt32 n[5]; | ||
329 | const char *sz; | ||
330 | }; | ||
331 | |||
332 | static const CServicesToName services_to_name[] = | ||
333 | { | ||
334 | { { 0x38FB89B5, 0xCBC28419, 0x6D236C5C, 0x6E770057, 0x876402C0 } , "TrustedInstaller" } | ||
335 | }; | ||
336 | |||
337 | static 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 | |||
420 | static 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 | |||
431 | static 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 | |||
505 | void 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 | |||
531 | static 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 | |||
545 | static 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 | |||
561 | bool 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 | |||
582 | static 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 | |||
611 | bool 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 | ||
9 | void ConvertPropertyToShortString2(char *dest, const PROPVARIANT &propVariant, PROPID propID, int level = 0) throw(); | ||
10 | void ConvertPropertyToString2(UString &dest, const PROPVARIANT &propVariant, PROPID propID, int level = 0); | ||
11 | |||
12 | bool ConvertNtReparseToString(const Byte *data, UInt32 size, UString &s); | ||
13 | void ConvertNtSecureToString(const Byte *data, UInt32 size, AString &s); | ||
14 | bool CheckNtSecure(const Byte *data, UInt32 size) throw();; | ||
15 | |||
16 | void 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 | |||
8 | struct 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 | |||
15 | using namespace NWindows; | ||
16 | using namespace NCOM; | ||
17 | |||
18 | static 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 | |||
31 | struct 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 | |||
45 | HRESULT 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 | |||
8 | HRESULT 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 | |||
9 | static 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 | |||
15 | void 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 | |||
8 | void 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 | |||
9 | using namespace NWindows; | ||
10 | using namespace NFile; | ||
11 | |||
12 | void 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 | |||
8 | class CTempFiles | ||
9 | { | ||
10 | void Clear(); | ||
11 | public: | ||
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 | |||
32 | static const char * const kUpdateIsNotSupoorted = | ||
33 | "update operations are not supported for this archive"; | ||
34 | |||
35 | static const char * const kUpdateIsNotSupported_MultiVol = | ||
36 | "Updating for multivolume archives is not implemented"; | ||
37 | |||
38 | using namespace NWindows; | ||
39 | using namespace NCOM; | ||
40 | using namespace NFile; | ||
41 | using namespace NDir; | ||
42 | using namespace NName; | ||
43 | |||
44 | #ifdef _WIN32 | ||
45 | static CFSTR const kTempFolderPrefix = FTEXT("7zE"); | ||
46 | #endif | ||
47 | |||
48 | void CUpdateErrorInfo::SetFromLastError(const char *message) | ||
49 | { | ||
50 | SystemError = ::GetLastError(); | ||
51 | Message = message; | ||
52 | } | ||
53 | |||
54 | HRESULT CUpdateErrorInfo::SetFromLastError(const char *message, const FString &fileName) | ||
55 | { | ||
56 | SetFromLastError(message); | ||
57 | FileNames.Add(fileName); | ||
58 | return Get_HRESULT_Error(); | ||
59 | } | ||
60 | |||
61 | HRESULT 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 | |||
70 | using namespace NUpdateArchive; | ||
71 | |||
72 | class 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; | ||
90 | public: | ||
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 | |||
118 | HRESULT 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 | |||
134 | bool 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 | |||
147 | STDMETHODIMP 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 | |||
224 | STDMETHODIMP 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 | |||
241 | STDMETHODIMP 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 | |||
270 | void 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 | |||
301 | UString CArchivePath::GetFinalPath() const | ||
302 | { | ||
303 | UString path = GetPathWithoutExt(); | ||
304 | if (!BaseExtension.IsEmpty()) | ||
305 | { | ||
306 | path += '.'; | ||
307 | path += BaseExtension; | ||
308 | } | ||
309 | return path; | ||
310 | } | ||
311 | |||
312 | UString 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 | |||
324 | FString 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 | |||
338 | static const char * const kDefaultArcType = "7z"; | ||
339 | static const char * const kDefaultArcExt = "7z"; | ||
340 | static const char * const kSFXExtension = | ||
341 | #ifdef _WIN32 | ||
342 | "exe"; | ||
343 | #else | ||
344 | ""; | ||
345 | #endif | ||
346 | |||
347 | bool 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 | |||
372 | bool 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 | |||
404 | struct 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 | |||
422 | HRESULT 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 | |||
443 | bool CRenamePair::Prepare() | ||
444 | { | ||
445 | if (RecursedType != NRecursedType::kNonRecursed) | ||
446 | return false; | ||
447 | if (!WildcardParsing) | ||
448 | return true; | ||
449 | return !DoesNameContainWildcard(OldName); | ||
450 | } | ||
451 | |||
452 | extern bool g_CaseSensitive; | ||
453 | |||
454 | static 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 | |||
472 | bool 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 | ||
497 | int FindAltStreamColon_in_Path(const wchar_t *path); | ||
498 | #endif | ||
499 | |||
500 | static 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 | |||
977 | static 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 | |||
988 | bool CensorNode_CheckPath2(const NWildcard::CCensorNode &node, const CReadArcItem &item, bool &include); | ||
989 | |||
990 | static 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 | |||
1010 | static 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 | |||
1082 | HRESULT 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 = ! | ||
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 | |||
17 | enum EArcNameMode | ||
18 | { | ||
19 | k_ArcNameMode_Smart, | ||
20 | k_ArcNameMode_Exact, | ||
21 | k_ArcNameMode_Add | ||
22 | }; | ||
23 | |||
24 | struct 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 | |||
46 | struct CUpdateArchiveCommand | ||
47 | { | ||
48 | UString UserArchivePath; | ||
49 | CArchivePath ArchivePath; | ||
50 | NUpdateArchive::CActionSet ActionSet; | ||
51 | }; | ||
52 | |||
53 | struct CCompressionMethodMode | ||
54 | { | ||
55 | bool Type_Defined; | ||
56 | COpenType Type; | ||
57 | CObjectVector<CProperty> Properties; | ||
58 | |||
59 | CCompressionMethodMode(): Type_Defined(false) {} | ||
60 | }; | ||
61 | |||
62 | namespace NRecursedType { enum EEnum | ||
63 | { | ||
64 | kRecursed, | ||
65 | kWildcardOnlyRecursed, | ||
66 | kNonRecursed | ||
67 | };} | ||
68 | |||
69 | struct 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 | |||
82 | struct 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 | |||
158 | struct 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 | |||
173 | struct 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 | |||
192 | struct IUpdateCallbackUI2: public IUpdateCallbackUI, public IDirItemsCallback | ||
193 | { | ||
194 | INTERFACE_IUpdateCallbackUI2(=0) | ||
195 | }; | ||
196 | |||
197 | HRESULT 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 | |||
7 | namespace NUpdateArchive { | ||
8 | |||
9 | const 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 | |||
20 | const 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 | |||
31 | const 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 | |||
42 | const 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 | |||
53 | const 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 | |||
6 | namespace 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 | |||
30 | using namespace NWindows; | ||
31 | using namespace NFile; | ||
32 | |||
33 | #ifndef _7ZIP_ST | ||
34 | static 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 | ||
42 | bool InitLocalPrivileges(); | ||
43 | #endif | ||
44 | |||
45 | CArchiveUpdateCallback::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 | |||
78 | STDMETHODIMP CArchiveUpdateCallback::SetTotal(UInt64 size) | ||
79 | { | ||
80 | COM_TRY_BEGIN | ||
81 | return Callback->SetTotal(size); | ||
82 | COM_TRY_END | ||
83 | } | ||
84 | |||
85 | STDMETHODIMP CArchiveUpdateCallback::SetCompleted(const UInt64 *completeValue) | ||
86 | { | ||
87 | COM_TRY_BEGIN | ||
88 | return Callback->SetCompleted(completeValue); | ||
89 | COM_TRY_END | ||
90 | } | ||
91 | |||
92 | STDMETHODIMP 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 | /* | ||
101 | static 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 | |||
113 | STDMETHODIMP CArchiveUpdateCallback::EnumProperties(IEnumSTATPROPSTG **) | ||
114 | { | ||
115 | return CStatPropEnumerator::CreateEnumerator(kProps, ARRAY_SIZE(kProps), enumerator); | ||
116 | } | ||
117 | */ | ||
118 | |||
119 | STDMETHODIMP 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 | |||
137 | STDMETHODIMP 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 | |||
153 | STDMETHODIMP CArchiveUpdateCallback::GetParent(UInt32 /* index */, UInt32 *parent, UInt32 *parentType) | ||
154 | { | ||
155 | *parentType = NParentType::kDir; | ||
156 | *parent = (UInt32)(Int32)-1; | ||
157 | return S_OK; | ||
158 | } | ||
159 | |||
160 | STDMETHODIMP CArchiveUpdateCallback::GetNumRawProps(UInt32 *numProps) | ||
161 | { | ||
162 | *numProps = 0; | ||
163 | if (StoreNtSecurity) | ||
164 | *numProps = 1; | ||
165 | return S_OK; | ||
166 | } | ||
167 | |||
168 | STDMETHODIMP CArchiveUpdateCallback::GetRawPropInfo(UInt32 /* index */, BSTR *name, PROPID *propID) | ||
169 | { | ||
170 | *name = NULL; | ||
171 | *propID = kpidNtSecure; | ||
172 | return S_OK; | ||
173 | } | ||
174 | |||
175 | STDMETHODIMP 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 | |||
213 | STDMETHODIMP 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 | |||
283 | static 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 | |||
326 | STDMETHODIMP 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 | ||
476 | static NSynchronization::CCriticalSection CS; | ||
477 | #endif | ||
478 | |||
479 | void 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 | |||
490 | STDMETHODIMP 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 | |||
625 | STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 opRes) | ||
626 | { | ||
627 | COM_TRY_BEGIN | ||
628 | return Callback->SetOperationResult(opRes); | ||
629 | COM_TRY_END | ||
630 | } | ||
631 | |||
632 | STDMETHODIMP 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 | |||
642 | STDMETHODIMP 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 | |||
700 | STDMETHODIMP 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 | |||
755 | STDMETHODIMP 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 | |||
765 | STDMETHODIMP 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 | |||
786 | STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password) | ||
787 | { | ||
788 | COM_TRY_BEGIN | ||
789 | return Callback->CryptoGetTextPassword2(passwordIsDefined, password); | ||
790 | COM_TRY_END | ||
791 | } | ||
792 | |||
793 | STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword(BSTR *password) | ||
794 | { | ||
795 | COM_TRY_BEGIN | ||
796 | return Callback->CryptoGetTextPassword(password); | ||
797 | COM_TRY_END | ||
798 | } | ||
799 | |||
800 | HRESULT 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 | |||
821 | void 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 | |||
18 | struct 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 | |||
50 | struct IUpdateCallbackUI | ||
51 | { | ||
52 | INTERFACE_IUpdateCallbackUI(=0) | ||
53 | }; | ||
54 | |||
55 | struct 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 | |||
70 | class 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 | |||
92 | public: | ||
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 | |||
14 | using namespace NWindows; | ||
15 | using namespace NTime; | ||
16 | |||
17 | static 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 | |||
41 | static const char * const k_Duplicate_inArc_Message = "Duplicate filename in archive:"; | ||
42 | static const char * const k_Duplicate_inDir_Message = "Duplicate filename on disk:"; | ||
43 | static const char * const k_NotCensoredCollision_Message = "Internal file name collision (file on disk, file in archive):"; | ||
44 | |||
45 | MY_ATTR_NORETURN | ||
46 | static | ||
47 | void 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 | |||
55 | static 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 | |||
65 | static 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 | |||
76 | void 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 | |||
11 | struct 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 | |||
21 | void 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 | |||
7 | using namespace NUpdateArchive; | ||
8 | |||
9 | static const char * const kUpdateActionSetCollision = "Internal collision in update action set"; | ||
10 | |||
11 | void 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 | |||
8 | struct 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 | |||
44 | struct IUpdateProduceCallback | ||
45 | { | ||
46 | virtual HRESULT ShowDeleteFile(unsigned arcIndex) = 0; | ||
47 | }; | ||
48 | |||
49 | void 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 | |||
13 | using namespace NWindows; | ||
14 | using namespace NFile; | ||
15 | using namespace NDir; | ||
16 | |||
17 | FString 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 | |||
67 | HRESULT 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 | |||
85 | HRESULT 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 | |||
12 | FString GetWorkDir(const NWorkDir::CInfo &workDirInfo, const FString &path, FString &fileName); | ||
13 | |||
14 | class CWorkDirTempFile | ||
15 | { | ||
16 | FString _originalPath; | ||
17 | NWindows::NFile::NDir::CTempFile _tempFile; | ||
18 | COutFileStream *_outStreamSpec; | ||
19 | public: | ||
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 | |||
16 | using namespace NWindows; | ||
17 | using namespace NRegistry; | ||
18 | |||
19 | static NSynchronization::CCriticalSection g_CS; | ||
20 | #define CS_LOCK NSynchronization::CCriticalSectionLock lock(g_CS); | ||
21 | |||
22 | static LPCTSTR const kCuPrefix = TEXT("Software") TEXT(STRING_PATH_SEPARATOR) TEXT("7-Zip") TEXT(STRING_PATH_SEPARATOR); | ||
23 | |||
24 | static CSysString GetKeyPath(LPCTSTR path) { return kCuPrefix + (CSysString)path; } | ||
25 | |||
26 | static LONG OpenMainKey(CKey &key, LPCTSTR keyName) | ||
27 | { | ||
28 | return key.Open(HKEY_CURRENT_USER, GetKeyPath(keyName), KEY_READ); | ||
29 | } | ||
30 | |||
31 | static LONG CreateMainKey(CKey &key, LPCTSTR keyName) | ||
32 | { | ||
33 | return key.Create(HKEY_CURRENT_USER, GetKeyPath(keyName)); | ||
34 | } | ||
35 | |||
36 | static void Key_Set_BoolPair(CKey &key, LPCTSTR name, const CBoolPair &b) | ||
37 | { | ||
38 | if (b.Def) | ||
39 | key.SetValue(name, b.Val); | ||
40 | } | ||
41 | |||
42 | static 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 | |||
48 | static 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 | |||
54 | namespace NExtract | ||
55 | { | ||
56 | |||
57 | static LPCTSTR const kKeyName = TEXT("Extraction"); | ||
58 | |||
59 | static LPCTSTR const kExtractMode = TEXT("ExtractMode"); | ||
60 | static LPCTSTR const kOverwriteMode = TEXT("OverwriteMode"); | ||
61 | static LPCTSTR const kShowPassword = TEXT("ShowPassword"); | ||
62 | static LPCTSTR const kPathHistory = TEXT("PathHistory"); | ||
63 | static LPCTSTR const kSplitDest = TEXT("SplitDest"); | ||
64 | static LPCTSTR const kElimDup = TEXT("ElimDup"); | ||
65 | // static LPCTSTR const kAltStreams = TEXT("AltStreams"); | ||
66 | static LPCTSTR const kNtSecur = TEXT("Security"); | ||
67 | |||
68 | void 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 | |||
89 | void Save_ShowPassword(bool showPassword) | ||
90 | { | ||
91 | CS_LOCK | ||
92 | CKey key; | ||
93 | CreateMainKey(key, kKeyName); | ||
94 | key.SetValue(kShowPassword, showPassword); | ||
95 | } | ||
96 | |||
97 | void 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 | |||
134 | bool 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 | |||
147 | namespace NCompression | ||
148 | { | ||
149 | |||
150 | static LPCTSTR const kKeyName = TEXT("Compression"); | ||
151 | |||
152 | static LPCTSTR const kArcHistory = TEXT("ArcHistory"); | ||
153 | static LPCWSTR const kArchiver = L"Archiver"; | ||
154 | static LPCTSTR const kShowPassword = TEXT("ShowPassword"); | ||
155 | static LPCTSTR const kEncryptHeaders = TEXT("EncryptHeaders"); | ||
156 | |||
157 | static LPCTSTR const kOptionsKeyName = TEXT("Options"); | ||
158 | |||
159 | static LPCTSTR const kLevel = TEXT("Level"); | ||
160 | static LPCTSTR const kDictionary = TEXT("Dictionary"); | ||
161 | static LPCTSTR const kOrder = TEXT("Order"); | ||
162 | static LPCTSTR const kBlockSize = TEXT("BlockSize"); | ||
163 | static LPCTSTR const kNumThreads = TEXT("NumThreads"); | ||
164 | static LPCWSTR const kMethod = L"Method"; | ||
165 | static LPCWSTR const kOptions = L"Options"; | ||
166 | static LPCWSTR const kEncryptionMethod = L"EncryptionMethod"; | ||
167 | |||
168 | static LPCTSTR const kNtSecur = TEXT("Security"); | ||
169 | static LPCTSTR const kAltStreams = TEXT("AltStreams"); | ||
170 | static LPCTSTR const kHardLinks = TEXT("HardLinks"); | ||
171 | static LPCTSTR const kSymLinks = TEXT("SymLinks"); | ||
172 | |||
173 | static 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 | |||
181 | static 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 | |||
189 | static void GetRegString(CKey &key, LPCWSTR name, UString &value) | ||
190 | { | ||
191 | if (key.QueryValue(name, value) != ERROR_SUCCESS) | ||
192 | value.Empty(); | ||
193 | } | ||
194 | |||
195 | static void GetRegUInt32(CKey &key, LPCTSTR name, UInt32 &value) | ||
196 | { | ||
197 | if (key.QueryValue(name, value) != ERROR_SUCCESS) | ||
198 | value = (UInt32)(Int32)-1; | ||
199 | } | ||
200 | |||
201 | static 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 | |||
208 | void 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 | |||
252 | void 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 | |||
314 | static 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 | |||
390 | void CMemUse::Parse(const UString &s) | ||
391 | { | ||
392 | IsDefined = ParseMemUse(s, *this); | ||
393 | } | ||
394 | |||
395 | /* | ||
396 | void MemLimit_Save(const UString &s) | ||
397 | { | ||
398 | CS_LOCK | ||
399 | CKey key; | ||
400 | CreateMainKey(key, kKeyName); | ||
401 | SetRegString(key, kMemUse, s); | ||
402 | } | ||
403 | |||
404 | bool 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 | |||
425 | static LPCTSTR const kOptionsInfoKeyName = TEXT("Options"); | ||
426 | |||
427 | namespace NWorkDir | ||
428 | { | ||
429 | static LPCTSTR const kWorkDirType = TEXT("WorkDirType"); | ||
430 | static LPCWSTR const kWorkDirPath = L"WorkDirPath"; | ||
431 | static LPCTSTR const kTempRemovableOnly = TEXT("TempRemovableOnly"); | ||
432 | |||
433 | |||
434 | void 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 | |||
444 | void 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 | |||
477 | static LPCTSTR const kCascadedMenu = TEXT("CascadedMenu"); | ||
478 | static LPCTSTR const kContextMenu = TEXT("ContextMenu"); | ||
479 | static LPCTSTR const kMenuIcons = TEXT("MenuIcons"); | ||
480 | static LPCTSTR const kElimDup = TEXT("ElimDupExtract"); | ||
481 | |||
482 | void 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 | |||
496 | void 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 | |||
13 | namespace 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 | |||
38 | namespace 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 | |||
119 | namespace 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 | |||
150 | struct 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 | ||