diff options
Diffstat (limited to '3rdparty/lzma/CPP/7zip/UI/Common')
48 files changed, 13367 insertions, 2923 deletions
diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveCommandLine.cpp b/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveCommandLine.cpp index 85ec17b42c9..1bd202edbcd 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveCommandLine.cpp +++ b/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveCommandLine.cpp @@ -1,6 +1,8 @@ // ArchiveCommandLine.cpp #include "StdAfx.h" +#undef printf +#undef sprintf #ifdef _WIN32 #ifndef UNDER_CE @@ -9,15 +11,15 @@ #endif #include <stdio.h> -#include "Common/ListFileUtils.h" -#include "Common/StringConvert.h" -#include "Common/StringToInt.h" +#include "../../../Common/ListFileUtils.h" +#include "../../../Common/StringConvert.h" +#include "../../../Common/StringToInt.h" -#include "Windows/FileDir.h" -#include "Windows/FileName.h" +#include "../../../Windows/FileDir.h" +#include "../../../Windows/FileName.h" #ifdef _WIN32 -#include "Windows/FileMapping.h" -#include "Windows/Synchronization.h" +#include "../../../Windows/FileMapping.h" +#include "../../../Windows/Synchronization.h" #endif #include "ArchiveCommandLine.h" @@ -48,6 +50,25 @@ using namespace NCommandLineParser; using namespace NWindows; using namespace NFile; +static bool StringToUInt32(const wchar_t *s, UInt32 &v) +{ + if (*s == 0) + return false; + const wchar_t *end; + v = ConvertStringToUInt32(s, &end); + return *end == 0; +} + +CArcCmdLineException::CArcCmdLineException(const char *a, const wchar_t *u) +{ + (*this) += MultiByteToUnicodeString(a); + if (u) + { + this->Add_LF(); + (*this) += u; + } +} + int g_CodePage = -1; namespace NKey { @@ -56,49 +77,98 @@ enum Enum kHelp1 = 0, kHelp2, kHelp3, + kDisableHeaders, kDisablePercents, - kArchiveType, + kShowTime, + kLogLevel, + + kOutStream, + kErrStream, + kPercentStream, + kYes, - #ifndef _NO_CRYPTO - kPassword, - #endif + + kShowDialog, + kOverwrite, + + kArchiveType, + kExcludedArcType, + kProperty, kOutputDir, kWorkingDir, + kInclude, kExclude, kArInclude, kArExclude, kNoArName, + kUpdate, kVolume, kRecursed, + + kAffinity, kSfx, + kEmail, + kHash, + kStdIn, kStdOut, - kOverwrite, - kEmail, - kShowDialog, + kLargePages, kListfileCharSet, kConsoleCharSet, kTechMode, + kShareForWrite, kCaseSensitive, - kCalcCrc + kArcNameMode, + + kDisableWildcardParsing, + kElimDup, + kFullPathMode, + + kHardLinks, + kSymLinks, + kNtSecurity, + kAltStreams, + kReplaceColonForAltStream, + kWriteToAltStreamIfColon, + + kDeleteAfterCompressing, + kSetArcMTime + + #ifndef _NO_CRYPTO + , kPassword + #endif }; } -static const wchar_t kRecursedIDChar = 'R'; -static const wchar_t *kRecursedPostCharSet = L"0-"; +static const wchar_t kRecursedIDChar = 'r'; +static const char *kRecursedPostCharSet = "0-"; + +static const char *k_ArcNameMode_PostCharSet = "sea"; + +static const char *k_Stream_PostCharSet = "012"; + +static inline const EArcNameMode ParseArcNameMode(int postCharIndex) +{ + switch (postCharIndex) + { + case 1: return k_ArcNameMode_Exact; + case 2: return k_ArcNameMode_Add; + default: return k_ArcNameMode_Smart; + } +} namespace NRecursedPostCharIndex { enum EEnum { - kWildCardRecursionOnly = 0, + kWildcardRecursionOnly = 0, kNoRecursion = 1 }; } @@ -110,135 +180,143 @@ static const char kFileListID = '@'; static const char kSomeCludePostStringMinSize = 2; // at least <@|!><N>ame must be static const char kSomeCludeAfterRecursedPostStringMinSize = 2; // at least <@|!><N>ame must be -static const wchar_t *kOverwritePostCharSet = L"asut"; +static const char *kOverwritePostCharSet = "asut"; -NExtract::NOverwriteMode::EEnum k_OverwriteModes[] = +static const NExtract::NOverwriteMode::EEnum k_OverwriteModes[] = { - NExtract::NOverwriteMode::kWithoutPrompt, - NExtract::NOverwriteMode::kSkipExisting, - NExtract::NOverwriteMode::kAutoRename, - NExtract::NOverwriteMode::kAutoRenameExisting + NExtract::NOverwriteMode::kOverwrite, + NExtract::NOverwriteMode::kSkip, + NExtract::NOverwriteMode::kRename, + NExtract::NOverwriteMode::kRenameExisting }; static const CSwitchForm kSwitchForms[] = - { - { L"?", NSwitchType::kSimple, false }, - { L"H", NSwitchType::kSimple, false }, - { L"-HELP", NSwitchType::kSimple, false }, - { L"BA", NSwitchType::kSimple, false }, - { L"BD", NSwitchType::kSimple, false }, - { L"T", NSwitchType::kUnLimitedPostString, false, 1 }, - { L"Y", NSwitchType::kSimple, false }, - #ifndef _NO_CRYPTO - { L"P", NSwitchType::kUnLimitedPostString, false, 0 }, - #endif - { L"M", NSwitchType::kUnLimitedPostString, true, 1 }, - { L"O", NSwitchType::kUnLimitedPostString, false, 1 }, - { L"W", NSwitchType::kUnLimitedPostString, false, 0 }, - { L"I", NSwitchType::kUnLimitedPostString, true, kSomeCludePostStringMinSize}, - { L"X", NSwitchType::kUnLimitedPostString, true, kSomeCludePostStringMinSize}, - { L"AI", NSwitchType::kUnLimitedPostString, true, kSomeCludePostStringMinSize}, - { L"AX", NSwitchType::kUnLimitedPostString, true, kSomeCludePostStringMinSize}, - { L"AN", NSwitchType::kSimple, false }, - { L"U", NSwitchType::kUnLimitedPostString, true, 1}, - { L"V", NSwitchType::kUnLimitedPostString, true, 1}, - { L"R", NSwitchType::kPostChar, false, 0, 0, kRecursedPostCharSet }, - { L"SFX", NSwitchType::kUnLimitedPostString, false, 0 }, - { L"SI", NSwitchType::kUnLimitedPostString, false, 0 }, - { L"SO", NSwitchType::kSimple, false, 0 }, - { L"AO", NSwitchType::kPostChar, false, 1, 1, kOverwritePostCharSet}, - { L"SEML", NSwitchType::kUnLimitedPostString, false, 0}, - { L"AD", NSwitchType::kSimple, false }, - { L"SLP", NSwitchType::kUnLimitedPostString, false, 0}, - { L"SCS", NSwitchType::kUnLimitedPostString, false, 0}, - { L"SCC", NSwitchType::kUnLimitedPostString, false, 0}, - { L"SLT", NSwitchType::kSimple, false }, - { L"SSW", NSwitchType::kSimple, false }, - { L"SSC", NSwitchType::kPostChar, false, 0, 0, L"-" }, - { L"SCRC", NSwitchType::kSimple, false } - }; +{ + { "?" }, + { "h" }, + { "-help" }, + + { "ba" }, + { "bd" }, + { "bt" }, + { "bb", NSwitchType::kString, false, 0 }, + + { "bso", NSwitchType::kChar, false, 1, k_Stream_PostCharSet }, + { "bse", NSwitchType::kChar, false, 1, k_Stream_PostCharSet }, + { "bsp", NSwitchType::kChar, false, 1, k_Stream_PostCharSet }, + + { "y" }, + + { "ad" }, + { "ao", NSwitchType::kChar, false, 1, kOverwritePostCharSet}, -static const CCommandForm g_CommandForms[] = -{ - { L"A", false }, - { L"U", false }, - { L"D", false }, - { L"T", false }, - { L"E", false }, - { L"X", false }, - { L"L", false }, - { L"B", false }, - { L"I", false } -}; + { "t", NSwitchType::kString, false, 1 }, + { "stx", NSwitchType::kString, true, 1 }, + + { "m", NSwitchType::kString, true, 1 }, + { "o", NSwitchType::kString, false, 1 }, + { "w", NSwitchType::kString }, + + { "i", NSwitchType::kString, true, kSomeCludePostStringMinSize}, + { "x", NSwitchType::kString, true, kSomeCludePostStringMinSize}, + { "ai", NSwitchType::kString, true, kSomeCludePostStringMinSize}, + { "ax", NSwitchType::kString, true, kSomeCludePostStringMinSize}, + { "an" }, + + { "u", NSwitchType::kString, true, 1}, + { "v", NSwitchType::kString, true, 1}, + { "r", NSwitchType::kChar, false, 0, kRecursedPostCharSet }, + + { "stm", NSwitchType::kString }, + { "sfx", NSwitchType::kString }, + { "seml", NSwitchType::kString, false, 0}, + { "scrc", NSwitchType::kString, true, 0 }, + + { "si", NSwitchType::kString }, + { "so" }, -static const int kNumCommandForms = sizeof(g_CommandForms) / sizeof(g_CommandForms[0]); + { "slp", NSwitchType::kMinus }, + { "scs", NSwitchType::kString }, + { "scc", NSwitchType::kString }, + { "slt" }, -static const wchar_t *kUniversalWildcard = L"*"; -static const int kMinNonSwitchWords = 1; -static const int kCommandIndex = 0; + { "ssw" }, + { "ssc", NSwitchType::kMinus }, + { "sa", NSwitchType::kChar, false, 1, k_ArcNameMode_PostCharSet }, + + { "spd" }, + { "spe", NSwitchType::kMinus }, + { "spf", NSwitchType::kString, false, 0 }, + + { "snh", NSwitchType::kMinus }, + { "snl", NSwitchType::kMinus }, + { "sni" }, + { "sns", NSwitchType::kMinus }, + { "snr" }, + { "snc" }, + + { "sdel" }, + { "stl" } + + #ifndef _NO_CRYPTO + , { "p", NSwitchType::kString } + #endif +}; -// --------------------------- -// exception messages +static const wchar_t *kUniversalWildcard = L"*"; +static const unsigned kMinNonSwitchWords = 1; +static const unsigned kCommandIndex = 0; -static const char *kUserErrorMessage = "Incorrect command line"; +// static const char *kUserErrorMessage = "Incorrect command line"; static const char *kCannotFindListFile = "Cannot find listfile"; static const char *kIncorrectListFile = "Incorrect item in listfile.\nCheck charset encoding and -scs switch."; -static const char *kIncorrectWildCardInListFile = "Incorrect wildcard in listfile"; -static const char *kIncorrectWildCardInCommandLine = "Incorrect wildcard in command line"; static const char *kTerminalOutError = "I won't write compressed data to a terminal"; -static const char *kSameTerminalError = "I won't write data and program's messages to same terminal"; +static const char *kSameTerminalError = "I won't write data and program's messages to same stream"; static const char *kEmptyFilePath = "Empty file path"; +static const char *kCannotFindArchive = "Cannot find archive"; -static void ThrowException(const char *errorMessage) +bool CArcCommand::IsFromExtractGroup() const { - throw CArchiveCommandLineException(errorMessage); -} - -static void ThrowUserErrorException() -{ - ThrowException(kUserErrorMessage); -} - -// --------------------------- - -bool CArchiveCommand::IsFromExtractGroup() const -{ - switch(CommandType) + switch (CommandType) { case NCommandType::kTest: case NCommandType::kExtract: - case NCommandType::kFullExtract: + case NCommandType::kExtractFull: return true; - default: - return false; } + return false; } -NExtract::NPathMode::EEnum CArchiveCommand::GetPathMode() const +NExtract::NPathMode::EEnum CArcCommand::GetPathMode() const { - switch(CommandType) + switch (CommandType) { case NCommandType::kTest: - case NCommandType::kFullExtract: - return NExtract::NPathMode::kFullPathnames; - default: - return NExtract::NPathMode::kNoPathnames; + case NCommandType::kExtractFull: + return NExtract::NPathMode::kFullPaths; } + return NExtract::NPathMode::kNoPaths; } -bool CArchiveCommand::IsFromUpdateGroup() const +bool CArcCommand::IsFromUpdateGroup() const { - return (CommandType == NCommandType::kAdd || - CommandType == NCommandType::kUpdate || - CommandType == NCommandType::kDelete); + switch (CommandType) + { + case NCommandType::kAdd: + case NCommandType::kUpdate: + case NCommandType::kDelete: + case NCommandType::kRename: + return true; + } + return false; } static NRecursedType::EEnum GetRecursedTypeFromIndex(int index) { switch (index) { - case NRecursedPostCharIndex::kWildCardRecursionOnly: - return NRecursedType::kWildCardOnlyRecursed; + case NRecursedPostCharIndex::kWildcardRecursionOnly: + return NRecursedType::kWildcardOnlyRecursed; case NRecursedPostCharIndex::kNoRecursion: return NRecursedType::kNonRecursed; default: @@ -246,170 +324,281 @@ static NRecursedType::EEnum GetRecursedTypeFromIndex(int index) } } -static bool ParseArchiveCommand(const UString &commandString, CArchiveCommand &command) +static const char *g_Commands = "audtexlbih"; + +static bool ParseArchiveCommand(const UString &commandString, CArcCommand &command) { - UString commandStringUpper = commandString; - commandStringUpper.MakeUpper(); - UString postString; - int commandIndex = ParseCommand(kNumCommandForms, g_CommandForms, commandStringUpper, - postString) ; - if (commandIndex < 0) - return false; - command.CommandType = (NCommandType::EEnum)commandIndex; - return true; + UString s = commandString; + s.MakeLower_Ascii(); + if (s.Len() == 1) + { + if (s[0] > 0x7F) + return false; + int index = FindCharPosInString(g_Commands, (char)s[0]); + if (index < 0) + return false; + command.CommandType = (NCommandType::EEnum)index; + return true; + } + if (s.Len() == 2 && s[0] == 'r' && s[1] == 'n') + { + command.CommandType = (NCommandType::kRename); + return true; + } + return false; } // ------------------------------------------------------------------ // filenames functions -static void AddNameToCensor(NWildcard::CCensor &wildcardCensor, - const UString &name, bool include, NRecursedType::EEnum type) +static void AddNameToCensor(NWildcard::CCensor &censor, + const UString &name, bool include, NRecursedType::EEnum type, bool wildcardMatching) { bool recursed = false; switch (type) { - case NRecursedType::kWildCardOnlyRecursed: - recursed = DoesNameContainWildCard(name); + case NRecursedType::kWildcardOnlyRecursed: + recursed = DoesNameContainWildcard(name); break; case NRecursedType::kRecursed: recursed = true; break; } - wildcardCensor.AddItem(include, name, recursed); + censor.AddPreItem(include, name, recursed, wildcardMatching); } -static void AddToCensorFromListFile(NWildcard::CCensor &wildcardCensor, - LPCWSTR fileName, bool include, NRecursedType::EEnum type, UINT codePage) +static void AddRenamePair(CObjectVector<CRenamePair> *renamePairs, + const UString &oldName, const UString &newName, NRecursedType::EEnum type, + bool wildcardMatching) +{ + CRenamePair &pair = renamePairs->AddNew(); + pair.OldName = oldName; + pair.NewName = newName; + pair.RecursedType = type; + pair.WildcardParsing = wildcardMatching; + + if (!pair.Prepare()) + { + UString val; + val += pair.OldName; + val.Add_LF(); + val += pair.NewName; + val.Add_LF(); + if (type == NRecursedType::kRecursed) + val.AddAscii("-r"); + else if (type == NRecursedType::kRecursed) + val.AddAscii("-r0"); + throw CArcCmdLineException("Unsupported rename command:", val); + } +} + +static void AddToCensorFromListFile( + CObjectVector<CRenamePair> *renamePairs, + NWildcard::CCensor &censor, + LPCWSTR fileName, bool include, NRecursedType::EEnum type, bool wildcardMatching, Int32 codePage) { UStringVector names; if (!NFind::DoesFileExist(us2fs(fileName))) - throw kCannotFindListFile; + throw CArcCmdLineException(kCannotFindListFile, fileName); if (!ReadNamesFromListFile(us2fs(fileName), names, codePage)) - throw kIncorrectListFile; - for (int i = 0; i < names.Size(); i++) - AddNameToCensor(wildcardCensor, names[i], include, type); + throw CArcCmdLineException(kIncorrectListFile, fileName); + if (renamePairs) + { + if ((names.Size() & 1) != 0) + throw CArcCmdLineException(kIncorrectListFile, fileName); + for (unsigned i = 0; i < names.Size(); i += 2) + { + // change type !!!! + AddRenamePair(renamePairs, names[i], names[i + 1], type, wildcardMatching); + } + } + else + FOR_VECTOR (i, names) + AddNameToCensor(censor, names[i], include, type, wildcardMatching); } static void AddToCensorFromNonSwitchesStrings( - int startIndex, - NWildcard::CCensor &wildcardCensor, + CObjectVector<CRenamePair> *renamePairs, + unsigned startIndex, + NWildcard::CCensor &censor, const UStringVector &nonSwitchStrings, NRecursedType::EEnum type, - bool thereAreSwitchIncludes, UINT codePage) + bool wildcardMatching, + bool thereAreSwitchIncludes, Int32 codePage) { - if (nonSwitchStrings.Size() == startIndex && (!thereAreSwitchIncludes)) - AddNameToCensor(wildcardCensor, kUniversalWildcard, true, type); - for (int i = startIndex; i < nonSwitchStrings.Size(); i++) + if ((renamePairs || nonSwitchStrings.Size() == startIndex) && !thereAreSwitchIncludes) + AddNameToCensor(censor, kUniversalWildcard, true, type, + true // wildcardMatching + ); + + int oldIndex = -1; + + for (unsigned i = startIndex; i < nonSwitchStrings.Size(); i++) { const UString &s = nonSwitchStrings[i]; if (s.IsEmpty()) - throw kEmptyFilePath; + throw CArcCmdLineException(kEmptyFilePath); if (s[0] == kFileListID) - AddToCensorFromListFile(wildcardCensor, s.Mid(1), true, type, codePage); - else - AddNameToCensor(wildcardCensor, s, true, type); - } -} - -#ifdef _WIN32 -static void ParseMapWithPaths(NWildcard::CCensor &wildcardCensor, - const UString &switchParam, bool include, - NRecursedType::EEnum commonRecursedType) -{ - int splitPos = switchParam.Find(L':'); - if (splitPos < 0) - ThrowUserErrorException(); - UString mappingName = switchParam.Left(splitPos); - - UString switchParam2 = switchParam.Mid(splitPos + 1); - splitPos = switchParam2.Find(L':'); - if (splitPos < 0) - ThrowUserErrorException(); - - UString mappingSize = switchParam2.Left(splitPos); - UString eventName = switchParam2.Mid(splitPos + 1); - - UInt64 dataSize64 = ConvertStringToUInt64(mappingSize, NULL); - UInt32 dataSize = (UInt32)dataSize64; - { - CFileMapping fileMapping; - if (fileMapping.Open(FILE_MAP_READ, GetSystemString(mappingName)) != 0) - ThrowException("Can not open mapping"); - LPVOID data = fileMapping.Map(FILE_MAP_READ, 0, dataSize); - if (data == NULL) - ThrowException("MapViewOfFile error"); - try + AddToCensorFromListFile(renamePairs, censor, s.Ptr(1), true, type, wildcardMatching, codePage); + else if (renamePairs) { - const wchar_t *curData = (const wchar_t *)data; - if (*curData != 0) - ThrowException("Incorrect mapping data"); - UInt32 numChars = dataSize / sizeof(wchar_t); - UString name; - for (UInt32 i = 1; i < numChars; i++) + if (oldIndex == -1) + oldIndex = i; + else { - wchar_t c = curData[i]; - if (c == L'\0') - { - AddNameToCensor(wildcardCensor, name, include, commonRecursedType); - name.Empty(); - } - else - name += c; + // NRecursedType::EEnum type is used for global wildcard (-i! switches) + AddRenamePair(renamePairs, nonSwitchStrings[oldIndex], s, NRecursedType::kNonRecursed, wildcardMatching); + // AddRenamePair(renamePairs, nonSwitchStrings[oldIndex], s, type); + oldIndex = -1; } - if (!name.IsEmpty()) - ThrowException("data error"); } - catch(...) - { - UnmapViewOfFile(data); - throw; - } - UnmapViewOfFile(data); + else + AddNameToCensor(censor, s, true, type, wildcardMatching); } + if (oldIndex != -1) + { + throw CArcCmdLineException("There is no second file name for rename pair:", nonSwitchStrings[oldIndex]); + } +} + +#ifdef _WIN32 + +struct CEventSetEnd +{ + UString Name; + + CEventSetEnd(const wchar_t *name): Name(name) {} + ~CEventSetEnd() { NSynchronization::CManualResetEvent event; - if (event.Open(EVENT_MODIFY_STATE, false, GetSystemString(eventName)) == S_OK) + if (event.Open(EVENT_MODIFY_STATE, false, GetSystemString(Name)) == 0) event.Set(); } +}; + +const char *k_IncorrectMapCommand = "Incorrect Map command"; + +static const char *ParseMapWithPaths( + NWildcard::CCensor &censor, + const UString &s2, bool include, + NRecursedType::EEnum commonRecursedType, + bool wildcardMatching) +{ + UString s = s2; + int pos = s.Find(L':'); + if (pos < 0) + return k_IncorrectMapCommand; + int pos2 = s.Find(L':', pos + 1); + if (pos2 < 0) + return k_IncorrectMapCommand; + + CEventSetEnd eventSetEnd((const wchar_t *)s + ((unsigned)pos2 + 1)); + s.DeleteFrom(pos2); + UInt32 size; + if (!StringToUInt32(s.Ptr(pos + 1), size) + || size < sizeof(wchar_t) + || size > ((UInt32)1 << 31) + || size % sizeof(wchar_t) != 0) + return "Unsupported Map data size"; + + s.DeleteFrom(pos); + CFileMapping map; + if (map.Open(FILE_MAP_READ, GetSystemString(s)) != 0) + return "Can not open mapping"; + LPVOID data = map.Map(FILE_MAP_READ, 0, size); + if (!data) + return "MapViewOfFile error"; + CFileUnmapper unmapper(data); + + UString name; + const wchar_t *p = (const wchar_t *)data; + if (*p != 0) // data format marker + return "Unsupported Map data"; + UInt32 numChars = size / sizeof(wchar_t); + for (UInt32 i = 1; i < numChars; i++) + { + wchar_t c = p[i]; + if (c == 0) + { + // MessageBoxW(0, name, L"7-Zip", 0); + AddNameToCensor(censor, name, include, commonRecursedType, wildcardMatching); + name.Empty(); + } + else + name += c; + } + if (!name.IsEmpty()) + return "Map data error"; + + return NULL; } + #endif -static void AddSwitchWildCardsToCensor(NWildcard::CCensor &wildcardCensor, +static void AddSwitchWildcardsToCensor( + NWildcard::CCensor &censor, const UStringVector &strings, bool include, - NRecursedType::EEnum commonRecursedType, UINT codePage) + NRecursedType::EEnum commonRecursedType, + bool wildcardMatching, + Int32 codePage) { - for (int i = 0; i < strings.Size(); i++) + const char *errorMessage = NULL; + unsigned i; + for (i = 0; i < strings.Size(); i++) { const UString &name = strings[i]; NRecursedType::EEnum recursedType; - int pos = 0; - if (name.Length() < kSomeCludePostStringMinSize) - ThrowUserErrorException(); - if (::MyCharUpper(name[pos]) == kRecursedIDChar) + unsigned pos = 0; + + if (name.Len() < kSomeCludePostStringMinSize) + { + errorMessage = "Too short switch"; + break; + } + + if (::MyCharLower_Ascii(name[pos]) == kRecursedIDChar) { pos++; - int index = UString(kRecursedPostCharSet).Find(name[pos]); + wchar_t c = name[pos]; + int index = -1; + if (c <= 0x7F) + index = FindCharPosInString(kRecursedPostCharSet, (char)c); recursedType = GetRecursedTypeFromIndex(index); if (index >= 0) pos++; } else recursedType = commonRecursedType; - if (name.Length() < pos + kSomeCludeAfterRecursedPostStringMinSize) - ThrowUserErrorException(); - UString tail = name.Mid(pos + 1); + + if (name.Len() < pos + kSomeCludeAfterRecursedPostStringMinSize) + { + errorMessage = "Too short switch"; + break; + } + + UString tail = name.Ptr(pos + 1); + if (name[pos] == kImmediateNameID) - AddNameToCensor(wildcardCensor, tail, include, recursedType); + AddNameToCensor(censor, tail, include, recursedType, wildcardMatching); else if (name[pos] == kFileListID) - AddToCensorFromListFile(wildcardCensor, tail, include, recursedType, codePage); + AddToCensorFromListFile(NULL, censor, tail, include, recursedType, wildcardMatching, codePage); #ifdef _WIN32 else if (name[pos] == kMapNameID) - ParseMapWithPaths(wildcardCensor, tail, include, recursedType); + { + errorMessage = ParseMapWithPaths(censor, tail, include, recursedType, wildcardMatching); + if (errorMessage) + break; + } #endif else - ThrowUserErrorException(); + { + errorMessage = "Incorrect wildcard type marker"; + break; + } } + if (i != strings.Size()) + throw CArcCmdLineException(errorMessage, strings[i]); } #ifdef _WIN32 @@ -418,20 +607,27 @@ static void AddSwitchWildCardsToCensor(NWildcard::CCensor &wildcardCensor, static void ConvertToLongName(const UString &prefix, UString &name) { - if (name.IsEmpty() || DoesNameContainWildCard(name)) + if (name.IsEmpty() || DoesNameContainWildcard(name)) return; NFind::CFileInfo fi; - if (fi.Find(us2fs(prefix + name))) + const FString path = us2fs(prefix + name); + #ifndef UNDER_CE + if (NFile::NName::IsDevicePath(path)) + return; + #endif + if (fi.Find(path)) name = fs2us(fi.Name); } static void ConvertToLongNames(const UString &prefix, CObjectVector<NWildcard::CItem> &items) { - for (int i = 0; i < items.Size(); i++) + FOR_VECTOR (i, items) { NWildcard::CItem &item = items[i]; if (item.Recursive || item.PathParts.Size() != 1) continue; + if (prefix.IsEmpty() && item.IsDriveItem()) + continue; ConvertToLongName(prefix, item.PathParts.Front()); } } @@ -440,17 +636,22 @@ static void ConvertToLongNames(const UString &prefix, NWildcard::CCensorNode &no { ConvertToLongNames(prefix, node.IncludeItems); ConvertToLongNames(prefix, node.ExcludeItems); - int i; + unsigned i; for (i = 0; i < node.SubNodes.Size(); i++) - ConvertToLongName(prefix, node.SubNodes[i].Name); + { + UString &name = node.SubNodes[i].Name; + if (prefix.IsEmpty() && NWildcard::IsDriveColonName(name)) + continue; + ConvertToLongName(prefix, name); + } // mix folders with same name for (i = 0; i < node.SubNodes.Size(); i++) { NWildcard::CCensorNode &nextNode1 = node.SubNodes[i]; - for (int j = i + 1; j < node.SubNodes.Size();) + for (unsigned j = i + 1; j < node.SubNodes.Size();) { const NWildcard::CCensorNode &nextNode2 = node.SubNodes[j]; - if (nextNode1.Name.CompareNoCase(nextNode2.Name) == 0) + if (nextNode1.Name.IsEqualTo_NoCase(nextNode2.Name)) { nextNode1.IncludeItems += nextNode2.IncludeItems; nextNode1.ExcludeItems += nextNode2.ExcludeItems; @@ -467,9 +668,9 @@ static void ConvertToLongNames(const UString &prefix, NWildcard::CCensorNode &no } } -static void ConvertToLongNames(NWildcard::CCensor &censor) +void ConvertToLongNames(NWildcard::CCensor &censor) { - for (int i = 0; i < censor.Pairs.Size(); i++) + FOR_VECTOR (i, censor.Pairs) { NWildcard::CPair &pair = censor.Pairs[i]; ConvertToLongNames(pair.Prefix, pair.Head); @@ -478,9 +679,10 @@ static void ConvertToLongNames(NWildcard::CCensor &censor) #endif +/* static NUpdateArchive::NPairAction::EEnum GetUpdatePairActionType(int i) { - switch(i) + switch (i) { case NUpdateArchive::NPairAction::kIgnore: return NUpdateArchive::NPairAction::kIgnore; case NUpdateArchive::NPairAction::kCopy: return NUpdateArchive::NPairAction::kCopy; @@ -489,36 +691,37 @@ static NUpdateArchive::NPairAction::EEnum GetUpdatePairActionType(int i) } throw 98111603; } +*/ -const UString kUpdatePairStateIDSet = L"PQRXYZW"; -const int kUpdatePairStateNotSupportedActions[] = {2, 2, 1, -1, -1, -1, -1}; - -const UString kUpdatePairActionIDSet = L"0123"; //Ignore, Copy, Compress, Create Anti +static const wchar_t *kUpdatePairStateIDSet = L"pqrxyzw"; +static const int kUpdatePairStateNotSupportedActions[] = {2, 2, 1, -1, -1, -1, -1}; -const wchar_t *kUpdateIgnoreItselfPostStringID = L"-"; -const wchar_t kUpdateNewArchivePostCharID = '!'; +static const unsigned kNumUpdatePairActions = 4; +static const char *kUpdateIgnoreItselfPostStringID = "-"; +static const wchar_t kUpdateNewArchivePostCharID = '!'; static bool ParseUpdateCommandString2(const UString &command, NUpdateArchive::CActionSet &actionSet, UString &postString) { - for (int i = 0; i < command.Length();) + for (unsigned i = 0; i < command.Len();) { - wchar_t c = MyCharUpper(command[i]); - int statePos = kUpdatePairStateIDSet.Find(c); + wchar_t c = MyCharLower_Ascii(command[i]); + int statePos = FindCharPosInString(kUpdatePairStateIDSet, c); if (statePos < 0) { - postString = command.Mid(i); + postString = command.Ptr(i); return true; } i++; - if (i >= command.Length()) + if (i >= command.Len()) return false; - int actionPos = kUpdatePairActionIDSet.Find(::MyCharUpper(command[i])); - if (actionPos < 0) + c = command[i]; + if (c < '0' || c >= '0' + kNumUpdatePairActions) return false; - actionSet.StateActions[statePos] = GetUpdatePairActionType(actionPos); - if (kUpdatePairStateNotSupportedActions[statePos] == actionPos) + unsigned actionPos = c - '0'; + actionSet.StateActions[(unsigned)statePos] = (NUpdateArchive::NPairAction::EEnum)(actionPos); + if (kUpdatePairStateNotSupportedActions[(unsigned)statePos] == (int)actionPos) return false; i++; } @@ -530,10 +733,12 @@ static void ParseUpdateCommandString(CUpdateOptions &options, const UStringVector &updatePostStrings, const NUpdateArchive::CActionSet &defaultActionSet) { - for (int i = 0; i < updatePostStrings.Size(); i++) + const char *errorMessage = "incorrect update switch command"; + unsigned i; + for (i = 0; i < updatePostStrings.Size(); i++) { const UString &updateString = updatePostStrings[i]; - if (updateString.CompareNoCase(kUpdateIgnoreItselfPostStringID) == 0) + if (updateString.IsEqualTo(kUpdateIgnoreItselfPostStringID)) { if (options.UpdateArchiveItself) { @@ -547,7 +752,7 @@ static void ParseUpdateCommandString(CUpdateOptions &options, UString postString; if (!ParseUpdateCommandString2(updateString, actionSet, postString)) - ThrowUserErrorException(); + break; if (postString.IsEmpty()) { if (options.UpdateArchiveItself) @@ -555,64 +760,23 @@ static void ParseUpdateCommandString(CUpdateOptions &options, } else { - if (MyCharUpper(postString[0]) != kUpdateNewArchivePostCharID) - ThrowUserErrorException(); + if (postString[0] != kUpdateNewArchivePostCharID) + break; CUpdateArchiveCommand uc; - UString archivePath = postString.Mid(1); + UString archivePath = postString.Ptr(1); if (archivePath.IsEmpty()) - ThrowUserErrorException(); + break; uc.UserArchivePath = archivePath; uc.ActionSet = actionSet; options.Commands.Add(uc); } } } + if (i != updatePostStrings.Size()) + throw CArcCmdLineException(errorMessage, updatePostStrings[i]); } -static const char kByteSymbol = 'B'; -static const char kKiloSymbol = 'K'; -static const char kMegaSymbol = 'M'; -static const char kGigaSymbol = 'G'; - -static bool ParseComplexSize(const UString &src, UInt64 &result) -{ - UString s = src; - s.MakeUpper(); - - const wchar_t *start = s; - const wchar_t *end; - UInt64 number = ConvertStringToUInt64(start, &end); - int numDigits = (int)(end - start); - if (numDigits == 0 || s.Length() > numDigits + 1) - return false; - if (s.Length() == numDigits) - { - result = number; - return true; - } - int numBits; - switch (s[numDigits]) - { - case kByteSymbol: - result = number; - return true; - case kKiloSymbol: - numBits = 10; - break; - case kMegaSymbol: - numBits = 20; - break; - case kGigaSymbol: - numBits = 30; - break; - default: - return false; - } - if (number >= ((UInt64)1 << (64 - numBits))) - return false; - result = number << numBits; - return true; -} +bool ParseComplexSize(const wchar_t *s, UInt64 &result); static void SetAddCommandOptions( NCommandType::EEnum commandType, @@ -620,16 +784,16 @@ static void SetAddCommandOptions( CUpdateOptions &options) { NUpdateArchive::CActionSet defaultActionSet; - switch(commandType) + switch (commandType) { case NCommandType::kAdd: - defaultActionSet = NUpdateArchive::kAddActionSet; + defaultActionSet = NUpdateArchive::k_ActionSet_Add; break; case NCommandType::kDelete: - defaultActionSet = NUpdateArchive::kDeleteActionSet; + defaultActionSet = NUpdateArchive::k_ActionSet_Delete; break; default: - defaultActionSet = NUpdateArchive::kUpdateActionSet; + defaultActionSet = NUpdateArchive::k_ActionSet_Update; } options.UpdateArchiveItself = true; @@ -645,7 +809,7 @@ static void SetAddCommandOptions( { const UString &postString = parser[NKey::kWorkingDir].PostStrings[0]; if (postString.IsEmpty()) - NDirectory::MyGetTempPath(options.WorkingDir); + NDir::MyGetTempPath(options.WorkingDir); else options.WorkingDir = us2fs(postString); } @@ -656,11 +820,11 @@ static void SetAddCommandOptions( if (parser[NKey::kVolume].ThereIs) { const UStringVector &sv = parser[NKey::kVolume].PostStrings; - for (int i = 0; i < sv.Size(); i++) + FOR_VECTOR (i, sv) { UInt64 size; - if (!ParseComplexSize(sv[i], size)) - ThrowException("Incorrect volume size"); + if (!ParseComplexSize(sv[i], size) || size == 0) + throw CArcCmdLineException("Incorrect volume size:", sv[i]); options.VolumesSizes.Add(size); } } @@ -670,116 +834,175 @@ static void SetMethodOptions(const CParser &parser, CObjectVector<CProperty> &pr { if (parser[NKey::kProperty].ThereIs) { - for (int i = 0; i < parser[NKey::kProperty].PostStrings.Size(); i++) + FOR_VECTOR (i, parser[NKey::kProperty].PostStrings) { - CProperty property; - const UString &postString = parser[NKey::kProperty].PostStrings[i]; - int index = postString.Find(L'='); - if (index < 0) - property.Name = postString; - else + CProperty prop; + prop.Name = parser[NKey::kProperty].PostStrings[i]; + int index = prop.Name.Find(L'='); + if (index >= 0) { - property.Name = postString.Left(index); - property.Value = postString.Mid(index + 1); + prop.Value = prop.Name.Ptr(index + 1); + prop.Name.DeleteFrom(index); } - properties.Add(property); + properties.Add(prop); } } } -CArchiveCommandLineParser::CArchiveCommandLineParser(): - parser(sizeof(kSwitchForms) / sizeof(kSwitchForms[0])) {} +CArcCmdLineParser::CArcCmdLineParser(): parser(ARRAY_SIZE(kSwitchForms)) {} -void CArchiveCommandLineParser::Parse1(const UStringVector &commandStrings, - CArchiveCommandLineOptions &options) +static inline void SetStreamMode(const CSwitchResult &sw, unsigned &res) { - try - { - parser.ParseStrings(kSwitchForms, commandStrings); - } - catch(...) - { - ThrowUserErrorException(); - } + if (sw.ThereIs) + res = sw.PostCharIndex; +} + +void CArcCmdLineParser::Parse1(const UStringVector &commandStrings, + CArcCmdLineOptions &options) +{ + if (!parser.ParseStrings(kSwitchForms, commandStrings)) + throw CArcCmdLineException(parser.ErrorMessage, parser.ErrorLine); options.IsInTerminal = MY_IS_TERMINAL(stdin); options.IsStdOutTerminal = MY_IS_TERMINAL(stdout); options.IsStdErrTerminal = MY_IS_TERMINAL(stderr); + + options.HelpMode = parser[NKey::kHelp1].ThereIs || parser[NKey::kHelp2].ThereIs || parser[NKey::kHelp3].ThereIs; + options.StdInMode = parser[NKey::kStdIn].ThereIs; options.StdOutMode = parser[NKey::kStdOut].ThereIs; options.EnableHeaders = !parser[NKey::kDisableHeaders].ThereIs; - options.HelpMode = parser[NKey::kHelp1].ThereIs || parser[NKey::kHelp2].ThereIs || parser[NKey::kHelp3].ThereIs; + options.TechMode = parser[NKey::kTechMode].ThereIs; + options.ShowTime = parser[NKey::kShowTime].ThereIs; + + if (parser[NKey::kDisablePercents].ThereIs + || options.StdOutMode + || !options.IsStdOutTerminal) + options.Number_for_Percents = k_OutStream_disabled; + + if (options.StdOutMode) + options.Number_for_Out = k_OutStream_disabled; + + SetStreamMode(parser[NKey::kOutStream], options.Number_for_Out); + SetStreamMode(parser[NKey::kErrStream], options.Number_for_Errors); + SetStreamMode(parser[NKey::kPercentStream], options.Number_for_Percents); + + if (parser[NKey::kLogLevel].ThereIs) + { + const UString &s = parser[NKey::kLogLevel].PostStrings[0]; + if (s.IsEmpty()) + options.LogLevel = 1; + else + { + UInt32 v; + if (!StringToUInt32(s, v)) + throw CArcCmdLineException("Unsupported switch postfix -bb", s); + options.LogLevel = (unsigned)v; + } + } + + if (parser[NKey::kCaseSensitive].ThereIs) + { + g_CaseSensitive = !parser[NKey::kCaseSensitive].WithMinus; + options.CaseSensitiveChange = true; + options.CaseSensitive = g_CaseSensitive; + } - #ifdef _WIN32 options.LargePages = false; if (parser[NKey::kLargePages].ThereIs) + options.LargePages = !parser[NKey::kLargePages].WithMinus; + + + #ifndef UNDER_CE + + if (parser[NKey::kAffinity].ThereIs) { - const UString &postString = parser[NKey::kLargePages].PostStrings.Front(); - if (postString.IsEmpty()) - options.LargePages = true; + const UString &s = parser[NKey::kAffinity].PostStrings[0]; + if (!s.IsEmpty()) + { + UInt32 v = 0; + AString a; + a.SetFromWStr_if_Ascii(s); + if (!a.IsEmpty()) + { + const char *end; + v = ConvertHexStringToUInt32(a, &end); + if (*end != 0) + a.Empty(); + } + if (a.IsEmpty()) + throw CArcCmdLineException("Unsupported switch postfix -stm", s); + + #ifdef _WIN32 + SetProcessAffinityMask(GetCurrentProcess(), v); + #endif + } } + #endif } struct CCodePagePair { - const wchar_t *Name; - UINT CodePage; + const char *Name; + Int32 CodePage; }; -static CCodePagePair g_CodePagePairs[] = +static const unsigned kNumByteOnlyCodePages = 3; + +static const CCodePagePair g_CodePagePairs[] = { - { L"UTF-8", CP_UTF8 }, - { L"WIN", CP_ACP }, - { L"DOS", CP_OEMCP } + { "utf-8", CP_UTF8 }, + { "win", CP_ACP }, + { "dos", CP_OEMCP }, + { "utf-16le", MY__CP_UTF16 }, + { "utf-16be", MY__CP_UTF16BE } }; -static int FindCharset(const NCommandLineParser::CParser &parser, int keyIndex, int defaultVal) +static Int32 FindCharset(const NCommandLineParser::CParser &parser, unsigned keyIndex, + bool byteOnlyCodePages, Int32 defaultVal) { if (!parser[keyIndex].ThereIs) return defaultVal; UString name = parser[keyIndex].PostStrings.Back(); - name.MakeUpper(); - int i; - for (i = 0; i < sizeof(g_CodePagePairs) / sizeof(g_CodePagePairs[0]); i++) + UInt32 v; + if (StringToUInt32(name, v)) + if (v < ((UInt32)1 << 16)) + return (Int32)v; + name.MakeLower_Ascii(); + unsigned num = byteOnlyCodePages ? kNumByteOnlyCodePages : ARRAY_SIZE(g_CodePagePairs); + for (unsigned i = 0;; i++) { + if (i == num) // to disable warnings from different compilers + throw CArcCmdLineException("Unsupported charset:", name); const CCodePagePair &pair = g_CodePagePairs[i]; - if (name.Compare(pair.Name) == 0) + if (name.IsEqualTo(pair.Name)) return pair.CodePage; } - if (i == sizeof(g_CodePagePairs) / sizeof(g_CodePagePairs[0])) - ThrowUserErrorException(); - return -1; } -static bool ConvertStringToUInt32(const wchar_t *s, UInt32 &v) -{ - const wchar_t *end; - UInt64 number = ConvertStringToUInt64(s, &end); - if (*end != 0) - return false; - if (number > (UInt32)0xFFFFFFFF) - return false; - v = (UInt32)number; - return true; -} - -void EnumerateDirItemsAndSort(NWildcard::CCensor &wildcardCensor, +HRESULT EnumerateDirItemsAndSort( + NWildcard::CCensor &censor, + NWildcard::ECensorPathMode censorPathMode, + const UString &addPathPrefix, UStringVector &sortedPaths, - UStringVector &sortedFullPaths) + UStringVector &sortedFullPaths, + CDirItemsStat &st, + IDirItemsCallback *callback) { - UStringVector paths; + FStringVector paths; + { CDirItems dirItems; + dirItems.Callback = callback; { - FStringVector errorPaths; - CRecordVector<DWORD> errorCodes; - HRESULT res = EnumerateItems(wildcardCensor, dirItems, NULL, errorPaths, errorCodes); - if (res != S_OK || errorPaths.Size() > 0) - throw "cannot find archive"; + HRESULT res = EnumerateItems(censor, censorPathMode, addPathPrefix, dirItems); + st = dirItems.Stat; + RINOK(res); } - for (int i = 0; i < dirItems.Items.Size(); i++) + + FOR_VECTOR (i, dirItems.Items) { const CDirItem &dirItem = dirItems.Items[i]; if (!dirItem.IsDir()) @@ -788,44 +1011,76 @@ void EnumerateDirItemsAndSort(NWildcard::CCensor &wildcardCensor, } if (paths.Size() == 0) - throw "there is no such archive"; + throw CArcCmdLineException(kCannotFindArchive); UStringVector fullPaths; - int i; + unsigned i; + for (i = 0; i < paths.Size(); i++) { FString fullPath; - NFile::NDirectory::MyGetFullPathName(us2fs(paths[i]), fullPath); + NFile::NDir::MyGetFullPathName(paths[i], fullPath); fullPaths.Add(fs2us(fullPath)); } - CIntVector indices; + + CUIntVector indices; SortFileNames(fullPaths, indices); - sortedPaths.Reserve(indices.Size()); - sortedFullPaths.Reserve(indices.Size()); + sortedPaths.ClearAndReserve(indices.Size()); + sortedFullPaths.ClearAndReserve(indices.Size()); + for (i = 0; i < indices.Size(); i++) { - int index = indices[i]; - sortedPaths.Add(paths[index]); - sortedFullPaths.Add(fullPaths[index]); + unsigned index = indices[i]; + sortedPaths.AddInReserved(fs2us(paths[index])); + sortedFullPaths.AddInReserved(fullPaths[index]); + if (i > 0 && CompareFileNames(sortedFullPaths[i], sortedFullPaths[i - 1]) == 0) + throw CArcCmdLineException("Duplicate archive path:", sortedFullPaths[i]); } + + return S_OK; } -void CArchiveCommandLineParser::Parse2(CArchiveCommandLineOptions &options) +static void SetBoolPair(NCommandLineParser::CParser &parser, unsigned switchID, CBoolPair &bp) +{ + bp.Def = parser[switchID].ThereIs; + if (bp.Def) + bp.Val = !parser[switchID].WithMinus; +} + +void CArcCmdLineParser::Parse2(CArcCmdLineOptions &options) { const UStringVector &nonSwitchStrings = parser.NonSwitchStrings; - int numNonSwitchStrings = nonSwitchStrings.Size(); + unsigned numNonSwitchStrings = nonSwitchStrings.Size(); if (numNonSwitchStrings < kMinNonSwitchWords) - ThrowUserErrorException(); + throw CArcCmdLineException("The command must be spcified"); if (!ParseArchiveCommand(nonSwitchStrings[kCommandIndex], options.Command)) - ThrowUserErrorException(); + throw CArcCmdLineException("Unsupported command:", nonSwitchStrings[kCommandIndex]); - options.TechMode = parser[NKey::kTechMode].ThereIs; - options.CalcCrc = parser[NKey::kCalcCrc].ThereIs; - - if (parser[NKey::kCaseSensitive].ThereIs) - g_CaseSensitive = (parser[NKey::kCaseSensitive].PostCharIndex < 0); + if (parser[NKey::kHash].ThereIs) + options.HashMethods = parser[NKey::kHash].PostStrings; + + if (parser[NKey::kElimDup].ThereIs) + { + options.ExtractOptions.ElimDup.Def = true; + options.ExtractOptions.ElimDup.Val = !parser[NKey::kElimDup].WithMinus; + } + + NWildcard::ECensorPathMode censorPathMode = NWildcard::k_RelatPath; + bool fullPathMode = parser[NKey::kFullPathMode].ThereIs; + if (fullPathMode) + { + censorPathMode = NWildcard::k_AbsPath; + const UString &s = parser[NKey::kFullPathMode].PostStrings[0]; + if (!s.IsEmpty()) + { + if (s == L"2") + censorPathMode = NWildcard::k_FullPath; + else + throw CArcCmdLineException("Unsupported -spf:", s); + } + } NRecursedType::EEnum recursedType; if (parser[NKey::kRecursed].ThereIs) @@ -833,43 +1088,58 @@ void CArchiveCommandLineParser::Parse2(CArchiveCommandLineOptions &options) else recursedType = NRecursedType::kNonRecursed; - g_CodePage = FindCharset(parser, NKey::kConsoleCharSet, -1); - UINT codePage = FindCharset(parser, NKey::kListfileCharSet, CP_UTF8); + bool wildcardMatching = true; + if (parser[NKey::kDisableWildcardParsing].ThereIs) + wildcardMatching = false; + + g_CodePage = FindCharset(parser, NKey::kConsoleCharSet, true, -1); + Int32 codePage = FindCharset(parser, NKey::kListfileCharSet, false, CP_UTF8); bool thereAreSwitchIncludes = false; + if (parser[NKey::kInclude].ThereIs) { thereAreSwitchIncludes = true; - AddSwitchWildCardsToCensor(options.WildcardCensor, - parser[NKey::kInclude].PostStrings, true, recursedType, codePage); + AddSwitchWildcardsToCensor(options.Censor, + parser[NKey::kInclude].PostStrings, true, recursedType, wildcardMatching, codePage); } + if (parser[NKey::kExclude].ThereIs) - AddSwitchWildCardsToCensor(options.WildcardCensor, - parser[NKey::kExclude].PostStrings, false, recursedType, codePage); + AddSwitchWildcardsToCensor(options.Censor, + parser[NKey::kExclude].PostStrings, false, recursedType, wildcardMatching, codePage); - int curCommandIndex = kCommandIndex + 1; + unsigned curCommandIndex = kCommandIndex + 1; bool thereIsArchiveName = !parser[NKey::kNoArName].ThereIs && options.Command.CommandType != NCommandType::kBenchmark && - options.Command.CommandType != NCommandType::kInfo; + options.Command.CommandType != NCommandType::kInfo && + options.Command.CommandType != NCommandType::kHash; bool isExtractGroupCommand = options.Command.IsFromExtractGroup(); bool isExtractOrList = isExtractGroupCommand || options.Command.CommandType == NCommandType::kList; + bool isRename = options.Command.CommandType == NCommandType::kRename; - if (isExtractOrList && options.StdInMode) + if ((isExtractOrList || isRename) && options.StdInMode) thereIsArchiveName = false; + if (parser[NKey::kArcNameMode].ThereIs) + options.UpdateOptions.ArcNameMode = ParseArcNameMode(parser[NKey::kArcNameMode].PostCharIndex); + if (thereIsArchiveName) { if (curCommandIndex >= numNonSwitchStrings) - ThrowUserErrorException(); + throw CArcCmdLineException("Cannot find archive name"); options.ArchiveName = nonSwitchStrings[curCommandIndex++]; if (options.ArchiveName.IsEmpty()) - ThrowUserErrorException(); + throw CArcCmdLineException("Archive name cannot by empty"); + #ifdef _WIN32 + // options.ArchiveName.Replace(L'/', WCHAR_PATH_SEPARATOR); + #endif } - AddToCensorFromNonSwitchesStrings( - curCommandIndex, options.WildcardCensor, - nonSwitchStrings, recursedType, thereAreSwitchIncludes, codePage); + AddToCensorFromNonSwitchesStrings(isRename ? &options.UpdateOptions.RenamePairs : NULL, + curCommandIndex, options.Censor, + nonSwitchStrings, recursedType, wildcardMatching, + thereAreSwitchIncludes, codePage); options.YesToAll = parser[NKey::kYes].ThereIs; @@ -885,63 +1155,126 @@ void CArchiveCommandLineParser::Parse2(CArchiveCommandLineOptions &options) if (parser[NKey::kArchiveType].ThereIs) options.ArcType = parser[NKey::kArchiveType].PostStrings[0]; + options.ExcludedArcTypes = parser[NKey::kExcludedArcType].PostStrings; + SetMethodOptions(parser, options.Properties); + if (parser[NKey::kNtSecurity].ThereIs) options.NtSecurity.SetTrueTrue(); + + SetBoolPair(parser, NKey::kAltStreams, options.AltStreams); + SetBoolPair(parser, NKey::kHardLinks, options.HardLinks); + SetBoolPair(parser, NKey::kSymLinks, options.SymLinks); + if (isExtractOrList) { - if (!options.WildcardCensor.AllAreRelative()) - ThrowException("Cannot use absolute pathnames for this command"); + CExtractOptionsBase &eo = options.ExtractOptions; + + { + CExtractNtOptions &nt = eo.NtOptions; + nt.NtSecurity = options.NtSecurity; + + nt.AltStreams = options.AltStreams; + if (!options.AltStreams.Def) + nt.AltStreams.Val = true; + + nt.HardLinks = options.HardLinks; + if (!options.HardLinks.Def) + nt.HardLinks.Val = true; - NWildcard::CCensor archiveWildcardCensor; + nt.SymLinks = options.SymLinks; + if (!options.SymLinks.Def) + nt.SymLinks.Val = true; + + nt.ReplaceColonForAltStream = parser[NKey::kReplaceColonForAltStream].ThereIs; + nt.WriteToAltStreamIfColon = parser[NKey::kWriteToAltStreamIfColon].ThereIs; + } + + options.Censor.AddPathsToCensor(NWildcard::k_AbsPath); + options.Censor.ExtendExclude(); + + // are there paths that look as non-relative (!Prefix.IsEmpty()) + if (!options.Censor.AllAreRelative()) + throw CArcCmdLineException("Cannot use absolute pathnames for this command"); + + NWildcard::CCensor &arcCensor = options.arcCensor; if (parser[NKey::kArInclude].ThereIs) - AddSwitchWildCardsToCensor(archiveWildcardCensor, - parser[NKey::kArInclude].PostStrings, true, NRecursedType::kNonRecursed, codePage); + AddSwitchWildcardsToCensor(arcCensor, parser[NKey::kArInclude].PostStrings, true, NRecursedType::kNonRecursed, wildcardMatching, codePage); if (parser[NKey::kArExclude].ThereIs) - AddSwitchWildCardsToCensor(archiveWildcardCensor, - parser[NKey::kArExclude].PostStrings, false, NRecursedType::kNonRecursed, codePage); + AddSwitchWildcardsToCensor(arcCensor, parser[NKey::kArExclude].PostStrings, false, NRecursedType::kNonRecursed, wildcardMatching, codePage); if (thereIsArchiveName) - AddNameToCensor(archiveWildcardCensor, options.ArchiveName, true, NRecursedType::kNonRecursed); + AddNameToCensor(arcCensor, options.ArchiveName, true, NRecursedType::kNonRecursed, wildcardMatching); + + arcCensor.AddPathsToCensor(NWildcard::k_RelatPath); #ifdef _WIN32 - ConvertToLongNames(archiveWildcardCensor); + ConvertToLongNames(arcCensor); #endif - archiveWildcardCensor.ExtendExclude(); + arcCensor.ExtendExclude(); if (options.StdInMode) - { - UString arcName = parser[NKey::kStdIn].PostStrings.Front(); - options.ArchivePathsSorted.Add(arcName); - options.ArchivePathsFullSorted.Add(arcName); - } - else - { - EnumerateDirItemsAndSort(archiveWildcardCensor, - options.ArchivePathsSorted, - options.ArchivePathsFullSorted); - } + options.ArcName_for_StdInMode = parser[NKey::kStdIn].PostStrings.Front(); if (isExtractGroupCommand) { - if (options.StdOutMode && options.IsStdOutTerminal && options.IsStdErrTerminal) - throw kSameTerminalError; + if (options.StdOutMode) + { + if ( + options.Number_for_Percents == k_OutStream_stdout + // || options.Number_for_Out == k_OutStream_stdout + // || options.Number_for_Errors == k_OutStream_stdout + || + ( + (options.IsStdOutTerminal && options.IsStdErrTerminal) + && + ( + options.Number_for_Percents != k_OutStream_disabled + // || options.Number_for_Out != k_OutStream_disabled + // || options.Number_for_Errors != k_OutStream_disabled + ) + ) + ) + throw CArcCmdLineException(kSameTerminalError); + } + if (parser[NKey::kOutputDir].ThereIs) { - options.OutputDir = us2fs(parser[NKey::kOutputDir].PostStrings[0]); - NFile::NName::NormalizeDirPathPrefix(options.OutputDir); + eo.OutputDir = us2fs(parser[NKey::kOutputDir].PostStrings[0]); + NFile::NName::NormalizeDirPathPrefix(eo.OutputDir); } - options.OverwriteMode = NExtract::NOverwriteMode::kAskBefore; + eo.OverwriteMode = NExtract::NOverwriteMode::kAsk; if (parser[NKey::kOverwrite].ThereIs) - options.OverwriteMode = k_OverwriteModes[parser[NKey::kOverwrite].PostCharIndex]; + { + eo.OverwriteMode = k_OverwriteModes[(unsigned)parser[NKey::kOverwrite].PostCharIndex]; + eo.OverwriteMode_Force = true; + } else if (options.YesToAll) - options.OverwriteMode = NExtract::NOverwriteMode::kWithoutPrompt; + { + eo.OverwriteMode = NExtract::NOverwriteMode::kOverwrite; + eo.OverwriteMode_Force = true; + } + } + + eo.PathMode = options.Command.GetPathMode(); + if (censorPathMode == NWildcard::k_AbsPath) + { + eo.PathMode = NExtract::NPathMode::kAbsPaths; + eo.PathMode_Force = true; + } + else if (censorPathMode == NWildcard::k_FullPath) + { + eo.PathMode = NExtract::NPathMode::kFullPaths; + eo.PathMode_Force = true; } } else if (options.Command.IsFromUpdateGroup()) { + if (parser[NKey::kArInclude].ThereIs) + throw CArcCmdLineException("-ai switch is not supported for this command"); + CUpdateOptions &updateOptions = options.UpdateOptions; SetAddCommandOptions(options.Command.CommandType, parser, updateOptions); @@ -951,20 +1284,18 @@ void CArchiveCommandLineParser::Parse2(CArchiveCommandLineOptions &options) if (parser[NKey::kShareForWrite].ThereIs) updateOptions.OpenShareForWrite = true; - options.EnablePercents = !parser[NKey::kDisablePercents].ThereIs; + updateOptions.PathMode = censorPathMode; - if (options.EnablePercents) - { - if ((options.StdOutMode && !options.IsStdErrTerminal) || - (!options.StdOutMode && !options.IsStdOutTerminal)) - options.EnablePercents = false; - } + updateOptions.AltStreams = options.AltStreams; + updateOptions.NtSecurity = options.NtSecurity; + updateOptions.HardLinks = options.HardLinks; + updateOptions.SymLinks = options.SymLinks; updateOptions.EMailMode = parser[NKey::kEmail].ThereIs; if (updateOptions.EMailMode) { updateOptions.EMailAddress = parser[NKey::kEmail].PostStrings.Front(); - if (updateOptions.EMailAddress.Length() > 0) + if (updateOptions.EMailAddress.Len() > 0) if (updateOptions.EMailAddress[0] == L'.') { updateOptions.EMailRemoveAfter = true; @@ -975,30 +1306,56 @@ void CArchiveCommandLineParser::Parse2(CArchiveCommandLineOptions &options) updateOptions.StdOutMode = options.StdOutMode; updateOptions.StdInMode = options.StdInMode; + updateOptions.DeleteAfterCompressing = parser[NKey::kDeleteAfterCompressing].ThereIs; + updateOptions.SetArcMTime = parser[NKey::kSetArcMTime].ThereIs; + if (updateOptions.StdOutMode && updateOptions.EMailMode) - throw "stdout mode and email mode cannot be combined"; - if (updateOptions.StdOutMode && options.IsStdOutTerminal) - throw kTerminalOutError; + throw CArcCmdLineException("stdout mode and email mode cannot be combined"); + + if (updateOptions.StdOutMode) + { + if (options.IsStdOutTerminal) + throw CArcCmdLineException(kTerminalOutError); + + if (options.Number_for_Percents == k_OutStream_stdout + || options.Number_for_Out == k_OutStream_stdout + || options.Number_for_Errors == k_OutStream_stdout) + throw CArcCmdLineException(kSameTerminalError); + } + if (updateOptions.StdInMode) updateOptions.StdInFileName = parser[NKey::kStdIn].PostStrings.Front(); - #ifdef _WIN32 - ConvertToLongNames(options.WildcardCensor); - #endif + if (options.Command.CommandType == NCommandType::kRename) + if (updateOptions.Commands.Size() != 1) + throw CArcCmdLineException("Only one archive can be created with rename command"); } else if (options.Command.CommandType == NCommandType::kBenchmark) { options.NumIterations = 1; if (curCommandIndex < numNonSwitchStrings) { - if (!ConvertStringToUInt32(nonSwitchStrings[curCommandIndex++], options.NumIterations)) - ThrowUserErrorException(); + if (!StringToUInt32(nonSwitchStrings[curCommandIndex], options.NumIterations)) + throw CArcCmdLineException("Incorrect Number of benmchmark iterations", nonSwitchStrings[curCommandIndex]); + curCommandIndex++; } } + else if (options.Command.CommandType == NCommandType::kHash) + { + options.Censor.AddPathsToCensor(censorPathMode); + options.Censor.ExtendExclude(); + + CHashOptions &hashOptions = options.HashOptions; + hashOptions.PathMode = censorPathMode; + hashOptions.Methods = options.HashMethods; + if (parser[NKey::kShareForWrite].ThereIs) + hashOptions.OpenShareForWrite = true; + hashOptions.StdInMode = options.StdInMode; + hashOptions.AltStreamsMode = options.AltStreams.Val; + } else if (options.Command.CommandType == NCommandType::kInfo) { } else - ThrowUserErrorException(); - options.WildcardCensor.ExtendExclude(); + throw 20150919; } diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveCommandLine.h b/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveCommandLine.h index 96646d605cf..a345505cb84 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveCommandLine.h +++ b/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveCommandLine.h @@ -3,15 +3,16 @@ #ifndef __ARCHIVE_COMMAND_LINE_H #define __ARCHIVE_COMMAND_LINE_H -#include "Common/CommandLineParser.h" -#include "Common/Wildcard.h" +#include "../../../Common/CommandLineParser.h" +#include "../../../Common/Wildcard.h" #include "Extract.h" +#include "HashCalc.h" #include "Update.h" -struct CArchiveCommandLineException: public AString +struct CArcCmdLineException: public UString { - CArchiveCommandLineException(const char *errorMessage): AString(errorMessage) {} + CArcCmdLineException(const char *a, const wchar_t *u = NULL); }; namespace NCommandType { enum EEnum @@ -21,35 +22,38 @@ namespace NCommandType { enum EEnum kDelete, kTest, kExtract, - kFullExtract, + kExtractFull, kList, kBenchmark, - kInfo + kInfo, + kHash, + kRename };} -namespace NRecursedType { enum EEnum -{ - kRecursed, - kWildCardOnlyRecursed, - kNonRecursed -};} - -struct CArchiveCommand +struct CArcCommand { NCommandType::EEnum CommandType; + bool IsFromExtractGroup() const; bool IsFromUpdateGroup() const; - bool IsTestMode() const { return CommandType == NCommandType::kTest; } + bool IsTestCommand() const { return CommandType == NCommandType::kTest; } NExtract::NPathMode::EEnum GetPathMode() const; }; -struct CArchiveCommandLineOptions +enum +{ + k_OutStream_disabled = 0, + k_OutStream_stdout = 1, + k_OutStream_stderr = 2 +}; + +struct CArcCmdLineOptions { bool HelpMode; - #ifdef _WIN32 bool LargePages; - #endif + bool CaseSensitiveChange; + bool CaseSensitive; bool IsInTerminal; bool IsStdOutTerminal; @@ -60,10 +64,9 @@ struct CArchiveCommandLineOptions bool YesToAll; bool ShowDialog; - // NWildcard::CCensor ArchiveWildcardCensor; - NWildcard::CCensor WildcardCensor; + NWildcard::CCensor Censor; - CArchiveCommand Command; + CArcCommand Command; UString ArchiveName; #ifndef _NO_CRYPTO @@ -72,36 +75,73 @@ struct CArchiveCommandLineOptions #endif bool TechMode; - // Extract - bool CalcCrc; + bool ShowTime; + + UStringVector HashMethods; + bool AppendName; - FString OutputDir; - NExtract::NOverwriteMode::EEnum OverwriteMode; - UStringVector ArchivePathsSorted; - UStringVector ArchivePathsFullSorted; + // UStringVector ArchivePathsSorted; + // UStringVector ArchivePathsFullSorted; + NWildcard::CCensor arcCensor; + UString ArcName_for_StdInMode; + CObjectVector<CProperty> Properties; + CExtractOptionsBase ExtractOptions; + + CBoolPair NtSecurity; + CBoolPair AltStreams; + CBoolPair HardLinks; + CBoolPair SymLinks; + CUpdateOptions UpdateOptions; + CHashOptions HashOptions; UString ArcType; - bool EnablePercents; + UStringVector ExcludedArcTypes; + + unsigned Number_for_Out; + unsigned Number_for_Errors; + unsigned Number_for_Percents; + unsigned LogLevel; + + // bool IsOutAllowed() const { return Number_for_Out != k_OutStream_disabled; } // Benchmark UInt32 NumIterations; - CArchiveCommandLineOptions(): StdInMode(false), StdOutMode(false) {}; + CArcCmdLineOptions(): + LargePages(false), + CaseSensitiveChange(false), + CaseSensitive(false), + + StdInMode(false), + StdOutMode(false), + + Number_for_Out(k_OutStream_stdout), + Number_for_Errors(k_OutStream_stderr), + Number_for_Percents(k_OutStream_stdout), + + LogLevel(0) + { + }; }; -class CArchiveCommandLineParser +class CArcCmdLineParser { NCommandLineParser::CParser parser; public: - CArchiveCommandLineParser(); - void Parse1(const UStringVector &commandStrings, CArchiveCommandLineOptions &options); - void Parse2(CArchiveCommandLineOptions &options); + CArcCmdLineParser(); + void Parse1(const UStringVector &commandStrings, CArcCmdLineOptions &options); + void Parse2(CArcCmdLineOptions &options); }; -void EnumerateDirItemsAndSort(NWildcard::CCensor &wildcardCensor, +HRESULT EnumerateDirItemsAndSort( + NWildcard::CCensor &censor, + NWildcard::ECensorPathMode pathMode, + const UString &addPathPrefix, UStringVector &sortedPaths, - UStringVector &sortedFullPaths); + UStringVector &sortedFullPaths, + CDirItemsStat &st, + IDirItemsCallback *callback); #endif diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp b/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp index 9c9c042291d..62da5e9fad4 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp +++ b/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp @@ -2,63 +2,273 @@ #include "StdAfx.h" -#include "Common/ComTry.h" -#include "Common/StringConvert.h" -#include "Common/Wildcard.h" +#undef sprintf +#undef printf -#include "Windows/FileDir.h" -#include "Windows/FileFind.h" -#include "Windows/FileName.h" -#include "Windows/PropVariant.h" -#include "Windows/PropVariantConversions.h" +#include "../../../../C/Alloc.h" + +#include "../../../Common/ComTry.h" +#include "../../../Common/IntToString.h" +#include "../../../Common/StringConvert.h" +#include "../../../Common/Wildcard.h" + +#include "../../../Windows/ErrorMsg.h" +#include "../../../Windows/FileDir.h" +#include "../../../Windows/FileFind.h" +#include "../../../Windows/FileName.h" +#include "../../../Windows/PropVariant.h" +#include "../../../Windows/PropVariantConv.h" + +#if defined(_WIN32) && !defined(UNDER_CE) && !defined(_SFX) +#define _USE_SECURITY_CODE +#include "../../../Windows/SecurityUtils.h" +#endif #include "../../Common/FilePathAutoRename.h" +// #include "../../Common/StreamUtils.h" #include "../Common/ExtractingFilePath.h" +#include "../Common/PropIDUtils.h" #include "ArchiveExtractCallback.h" using namespace NWindows; +using namespace NFile; +using namespace NDir; + +static const char *kCantAutoRename = "Can not create file with auto name"; +static const char *kCantRenameFile = "Can not rename existing file"; +static const char *kCantDeleteOutputFile = "Can not delete output file"; +static const char *kCantDeleteOutputDir = "Can not delete output folder"; + + +#ifndef _SFX + +STDMETHODIMP COutStreamWithHash::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + HRESULT result = S_OK; + if (_stream) + result = _stream->Write(data, size, &size); + if (_calculate) + _hash->Update(data, size); + _size += size; + if (processedSize) + *processedSize = size; + return result; +} + +#endif + +#ifdef _USE_SECURITY_CODE +bool InitLocalPrivileges() +{ + NSecurity::CAccessToken token; + if (!token.OpenProcessToken(GetCurrentProcess(), + TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES)) + return false; + + TOKEN_PRIVILEGES tp; + + tp.PrivilegeCount = 1; + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + if (!::LookupPrivilegeValue(NULL, SE_SECURITY_NAME, &tp.Privileges[0].Luid)) + return false; + if (!token.AdjustPrivileges(&tp)) + return false; + return (GetLastError() == ERROR_SUCCESS); +} +#endif + +#ifdef SUPPORT_LINKS + +int CHardLinkNode::Compare(const CHardLinkNode &a) const +{ + if (StreamId < a.StreamId) return -1; + if (StreamId > a.StreamId) return 1; + return MyCompare(INode, a.INode); +} + +static HRESULT Archive_Get_HardLinkNode(IInArchive *archive, UInt32 index, CHardLinkNode &h, bool &defined) +{ + h.INode = 0; + h.StreamId = (UInt64)(Int64)-1; + defined = false; + { + NCOM::CPropVariant prop; + RINOK(archive->GetProperty(index, kpidINode, &prop)); + if (!ConvertPropVariantToUInt64(prop, h.INode)) + return S_OK; + } + { + NCOM::CPropVariant prop; + RINOK(archive->GetProperty(index, kpidStreamId, &prop)); + ConvertPropVariantToUInt64(prop, h.StreamId); + } + defined = true; + return S_OK; +} + + +HRESULT CArchiveExtractCallback::PrepareHardLinks(const CRecordVector<UInt32> *realIndices) +{ + _hardLinks.Clear(); + + if (!_arc->Ask_INode) + return S_OK; + + IInArchive *archive = _arc->Archive; + CRecordVector<CHardLinkNode> &hardIDs = _hardLinks.IDs; + + { + UInt32 numItems; + if (realIndices) + numItems = realIndices->Size(); + else + { + RINOK(archive->GetNumberOfItems(&numItems)); + } + + for (UInt32 i = 0; i < numItems; i++) + { + CHardLinkNode h; + bool defined; + UInt32 realIndex = realIndices ? (*realIndices)[i] : i; + + RINOK(Archive_Get_HardLinkNode(archive, realIndex, h, defined)); + if (defined) + { + bool isAltStream = false; + RINOK(Archive_IsItem_AltStream(archive, realIndex, isAltStream)); + if (!isAltStream) + hardIDs.Add(h); + } + } + } + + hardIDs.Sort2(); + + { + // wee keep only items that have 2 or more items + unsigned k = 0; + unsigned numSame = 1; + for (unsigned i = 1; i < hardIDs.Size(); i++) + { + if (hardIDs[i].Compare(hardIDs[i - 1]) != 0) + numSame = 1; + else if (++numSame == 2) + { + if (i - 1 != k) + hardIDs[k] = hardIDs[i - 1]; + k++; + } + } + hardIDs.DeleteFrom(k); + } + + _hardLinks.PrepareLinks(); + return S_OK; +} -static const char *kCantAutoRename = "ERROR: Can not create file with auto name"; -static const char *kCantRenameFile = "ERROR: Can not rename existing file "; -static const char *kCantDeleteOutputFile = "ERROR: Can not delete output file "; +#endif + +CArchiveExtractCallback::CArchiveExtractCallback(): + WriteCTime(true), + WriteATime(true), + WriteMTime(true), + _multiArchives(false) +{ + LocalProgressSpec = new CLocalProgress(); + _localProgress = LocalProgressSpec; + + #ifdef _USE_SECURITY_CODE + _saclEnabled = InitLocalPrivileges(); + #endif +} void CArchiveExtractCallback::Init( + const CExtractNtOptions &ntOptions, const NWildcard::CCensorNode *wildcardCensor, const CArc *arc, IFolderArchiveExtractCallback *extractCallback2, - bool stdOutMode, bool testMode, bool crcMode, + bool stdOutMode, bool testMode, const FString &directoryPath, - const UStringVector &removePathParts, + const UStringVector &removePathParts, bool removePartsForAltStreams, UInt64 packSize) { + _extractedFolderPaths.Clear(); + _extractedFolderIndices.Clear(); + + #ifdef SUPPORT_LINKS + _hardLinks.Clear(); + #endif + + #ifdef SUPPORT_ALT_STREAMS + _renamedFiles.Clear(); + #endif + + _ntOptions = ntOptions; _wildcardCensor = wildcardCensor; _stdOutMode = stdOutMode; _testMode = testMode; - _crcMode = crcMode; - _unpTotal = 1; + + // _progressTotal = 0; + // _progressTotal_Defined = false; + _packTotal = packSize; + _progressTotal = packSize; + _progressTotal_Defined = true; _extractCallback2 = extractCallback2; _compressProgress.Release(); _extractCallback2.QueryInterface(IID_ICompressProgressInfo, &_compressProgress); + _extractCallback2.QueryInterface(IID_IArchiveExtractCallbackMessage, &_callbackMessage); + _extractCallback2.QueryInterface(IID_IFolderArchiveExtractCallback2, &_folderArchiveExtractCallback2); + + #ifndef _SFX + + _extractCallback2.QueryInterface(IID_IFolderExtractToStreamCallback, &ExtractToStreamCallback); + if (ExtractToStreamCallback) + { + Int32 useStreams = 0; + if (ExtractToStreamCallback->UseExtractToStream(&useStreams) != S_OK) + useStreams = 0; + if (useStreams == 0) + ExtractToStreamCallback.Release(); + } + + #endif LocalProgressSpec->Init(extractCallback2, true); LocalProgressSpec->SendProgress = false; - _removePathParts = removePathParts; + _removePartsForAltStreams = removePartsForAltStreams; + + #ifndef _SFX + _baseParentFolder = (UInt32)(Int32)-1; + _use_baseParentFolder_mode = false; + #endif + _arc = arc; - _directoryPath = directoryPath; - NFile::NName::NormalizeDirPathPrefix(_directoryPath); + _dirPathPrefix = directoryPath; + _dirPathPrefix_Full = directoryPath; + #if defined(_WIN32) && !defined(UNDER_CE) + if (!NName::IsAltPathPrefix(_dirPathPrefix)) + #endif + { + NName::NormalizeDirPathPrefix(_dirPathPrefix); + NDir::MyGetFullPathName(directoryPath, _dirPathPrefix_Full); + NName::NormalizeDirPathPrefix(_dirPathPrefix_Full); + } } STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 size) { COM_TRY_BEGIN - _unpTotal = size; + _progressTotal = size; + _progressTotal_Defined = true; if (!_multiArchives && _extractCallback2) return _extractCallback2->SetTotal(size); return S_OK; @@ -87,18 +297,20 @@ static UInt64 MyMultDiv64(UInt64 unpCur, UInt64 unpTotal, UInt64 packTotal) STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 *completeValue) { COM_TRY_BEGIN + if (!_extractCallback2) return S_OK; + UInt64 packCur; if (_multiArchives) { - if (completeValue != NULL) - { - UInt64 packCur = LocalProgressSpec->InSize + MyMultDiv64(*completeValue, _unpTotal, _packTotal); - return _extractCallback2->SetCompleted(&packCur); - } + packCur = LocalProgressSpec->InSize; + if (completeValue && _progressTotal_Defined) + packCur += MyMultDiv64(*completeValue, _progressTotal, _packTotal); + completeValue = &packCur; } return _extractCallback2->SetCompleted(completeValue); + COM_TRY_END } @@ -111,13 +323,39 @@ STDMETHODIMP CArchiveExtractCallback::SetRatioInfo(const UInt64 *inSize, const U void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPathParts, FString &fullPath) { - fullPath = _directoryPath; - for (int i = 0; i < dirPathParts.Size(); i++) + bool isAbsPath = false; + + if (!dirPathParts.IsEmpty()) + { + const UString &s = dirPathParts[0]; + if (s.IsEmpty()) + isAbsPath = true; + #if defined(_WIN32) && !defined(UNDER_CE) + else + { + if (NName::IsDrivePath2(s)) + isAbsPath = true; + } + #endif + } + + if (_pathMode == NExtract::NPathMode::kAbsPaths && isAbsPath) + fullPath.Empty(); + else + fullPath = _dirPathPrefix; + + FOR_VECTOR (i, dirPathParts) { - if (i > 0) - fullPath += FCHAR_PATH_SEPARATOR; - fullPath += us2fs(dirPathParts[i]); - NFile::NDirectory::MyCreateDirectory(fullPath); + if (i != 0) + fullPath.Add_PathSepar(); + const UString &s = dirPathParts[i]; + fullPath += us2fs(s); + #if defined(_WIN32) && !defined(UNDER_CE) + if (_pathMode == NExtract::NPathMode::kAbsPaths) + if (i == 0 && s.Len() == 2 && NName::IsDrivePath2(s)) + continue; + #endif + CreateDir(fullPath); } } @@ -141,31 +379,272 @@ HRESULT CArchiveExtractCallback::GetUnpackSize() return _arc->GetItemSize(_index, _curSize, _curSizeDefined); } +static void AddPathToMessage(UString &s, const FString &path) +{ + s.AddAscii(" : "); + s += fs2us(path); +} + HRESULT CArchiveExtractCallback::SendMessageError(const char *message, const FString &path) { - return _extractCallback2->MessageError(GetUnicodeString(message) + fs2us(path)); + UString s; + s.AddAscii(message); + AddPathToMessage(s, path); + return _extractCallback2->MessageError(s); +} + +HRESULT CArchiveExtractCallback::SendMessageError_with_LastError(const char *message, const FString &path) +{ + DWORD errorCode = GetLastError(); + UString s; + s.AddAscii(message); + if (errorCode != 0) + { + s.AddAscii(" : "); + s += NError::MyFormatMessage(errorCode); + } + AddPathToMessage(s, path); + return _extractCallback2->MessageError(s); +} + +HRESULT CArchiveExtractCallback::SendMessageError2(const char *message, const FString &path1, const FString &path2) +{ + UString s; + s.AddAscii(message); + AddPathToMessage(s, path1); + AddPathToMessage(s, path2); + return _extractCallback2->MessageError(s); +} + +#ifndef _SFX + +STDMETHODIMP CGetProp::GetProp(PROPID propID, PROPVARIANT *value) +{ + /* + if (propID == kpidName) + { + COM_TRY_BEGIN + NCOM::CPropVariant prop = Name; + prop.Detach(value); + return S_OK; + COM_TRY_END + } + */ + return Arc->Archive->GetProperty(IndexInArc, propID, value); +} + +#endif + + +#ifdef SUPPORT_LINKS + +static UString GetDirPrefixOf(const UString &src) +{ + UString s = src; + if (!s.IsEmpty()) + { + if (IsPathSepar(s.Back())) + s.DeleteBack(); + int pos = s.ReverseFind_PathSepar(); + s.DeleteFrom(pos + 1); + } + return s; +} + +#endif + + +bool IsSafePath(const UString &path) +{ + if (NName::IsAbsolutePath(path)) + return false; + + UStringVector parts; + SplitPathToParts(path, parts); + unsigned level = 0; + + FOR_VECTOR (i, parts) + { + const UString &s = parts[i]; + if (s.IsEmpty()) + { + if (i == 0) + return false; + continue; + } + if (s == L".") + continue; + if (s == L"..") + { + if (level == 0) + return false; + level--; + } + else + level++; + } + + return level > 0; +} + + +bool CensorNode_CheckPath2(const NWildcard::CCensorNode &node, const CReadArcItem &item, bool &include) +{ + bool found = false; + + if (node.CheckPathVect(item.PathParts, !item.MainIsDir, include)) + { + if (!include) + return true; + + #ifdef SUPPORT_ALT_STREAMS + if (!item.IsAltStream) + return true; + #endif + + found = true; + } + + #ifdef SUPPORT_ALT_STREAMS + + if (!item.IsAltStream) + return false; + + UStringVector pathParts2 = item.PathParts; + if (pathParts2.IsEmpty()) + pathParts2.AddNew(); + UString &back = pathParts2.Back(); + back += L':'; + back += item.AltStreamName; + bool include2; + + if (node.CheckPathVect(pathParts2, + true, // isFile, + include2)) + { + include = include2; + return true; + } + + #endif + + return found; +} + +bool CensorNode_CheckPath(const NWildcard::CCensorNode &node, const CReadArcItem &item) +{ + bool include; + if (CensorNode_CheckPath2(node, item, include)) + return include; + return false; +} + +static FString MakePath_from_2_Parts(const FString &prefix, const FString &path) +{ + FString s = prefix; + #if defined(_WIN32) && !defined(UNDER_CE) + if (!path.IsEmpty() && path[0] == ':' && !prefix.IsEmpty() && IsPathSepar(prefix.Back())) + { + if (!NName::IsDriveRootPath_SuperAllowed(prefix)) + s.DeleteBack(); + } + #endif + s += path; + return s; } + +/* +#ifdef SUPPORT_LINKS + +struct CTempMidBuffer +{ + void *Buf; + + CTempMidBuffer(size_t size): Buf(NULL) { Buf = ::MidAlloc(size); } + ~CTempMidBuffer() { ::MidFree(Buf); } +}; + +HRESULT CArchiveExtractCallback::MyCopyFile(ISequentialOutStream *outStream) +{ + const size_t kBufSize = 1 << 16; + CTempMidBuffer buf(kBufSize); + if (!buf.Buf) + return E_OUTOFMEMORY; + + NIO::CInFile inFile; + NIO::COutFile outFile; + + if (!inFile.Open(_CopyFile_Path)) + return SendMessageError_with_LastError("Open error", _CopyFile_Path); + + for (;;) + { + UInt32 num; + + if (!inFile.Read(buf.Buf, kBufSize, num)) + return SendMessageError_with_LastError("Read error", _CopyFile_Path); + + if (num == 0) + return S_OK; + + + RINOK(WriteStream(outStream, buf.Buf, num)); + } +} + +#endif +*/ + STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode) { COM_TRY_BEGIN - _crcStream.Release(); - *outStream = 0; + + *outStream = NULL; + + #ifndef _SFX + if (_hashStream) + _hashStreamSpec->ReleaseStream(); + _hashStreamWasUsed = false; + #endif + _outFileStream.Release(); _encrypted = false; + _position = 0; _isSplit = false; + _curSize = 0; _curSizeDefined = false; _index = index; - UString fullPath; + _diskFilePath.Empty(); + + // _fi.Clear(); + + #ifdef SUPPORT_LINKS + // _CopyFile_Path.Empty(); + linkPath.Empty(); + #endif IInArchive *archive = _arc->Archive; - RINOK(_arc->GetItemPath(index, fullPath)); - RINOK(IsArchiveItemFolder(archive, index, _fi.IsDir)); - _filePath = fullPath; + #ifndef _SFX + _item._use_baseParentFolder_mode = _use_baseParentFolder_mode; + if (_use_baseParentFolder_mode) + { + _item._baseParentFolder = _baseParentFolder; + if (_pathMode == NExtract::NPathMode::kFullPaths || + _pathMode == NExtract::NPathMode::kAbsPaths) + _item._baseParentFolder = -1; + } + #endif + + #ifdef SUPPORT_ALT_STREAMS + _item.WriteToAltStreamIfColon = _ntOptions.WriteToAltStreamIfColon; + #endif + + RINOK(_arc->GetItem(index, _item)); { NCOM::CPropVariant prop; @@ -178,26 +657,303 @@ STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStre _isSplit = true; } } + + #ifdef SUPPORT_LINKS + + // bool isCopyLink = false; + bool isHardLink = false; + bool isJunction = false; + bool isRelative = false; + + { + NCOM::CPropVariant prop; + RINOK(archive->GetProperty(index, kpidHardLink, &prop)); + if (prop.vt == VT_BSTR) + { + isHardLink = true; + // isCopyLink = false; + isRelative = false; // RAR5, TAR: hard links are from root folder of archive + linkPath.SetFromBstr(prop.bstrVal); + } + else if (prop.vt != VT_EMPTY) + return E_FAIL; + } + + /* + { + NCOM::CPropVariant prop; + RINOK(archive->GetProperty(index, kpidCopyLink, &prop)); + if (prop.vt == VT_BSTR) + { + isHardLink = false; + isCopyLink = true; + isRelative = false; // RAR5: copy links are from root folder of archive + linkPath.SetFromBstr(prop.bstrVal); + } + else if (prop.vt != VT_EMPTY) + return E_FAIL; + } + */ + + { + NCOM::CPropVariant prop; + RINOK(archive->GetProperty(index, kpidSymLink, &prop)); + if (prop.vt == VT_BSTR) + { + isHardLink = false; + // isCopyLink = false; + isRelative = true; // RAR5, TAR: symbolic links can be relative + linkPath.SetFromBstr(prop.bstrVal); + } + else if (prop.vt != VT_EMPTY) + return E_FAIL; + } + + + bool isOkReparse = false; + + if (linkPath.IsEmpty() && _arc->GetRawProps) + { + const void *data; + UInt32 dataSize; + UInt32 propType; - RINOK(GetArchiveItemBoolProp(archive, index, kpidEncrypted, _encrypted)); + _arc->GetRawProps->GetRawProp(_index, kpidNtReparse, &data, &dataSize, &propType); + + if (dataSize != 0) + { + if (propType != NPropDataType::kRaw) + return E_FAIL; + UString s; + CReparseAttr reparse; + isOkReparse = reparse.Parse((const Byte *)data, dataSize); + if (isOkReparse) + { + isHardLink = false; + // isCopyLink = false; + linkPath = reparse.GetPath(); + isJunction = reparse.IsMountPoint(); + isRelative = reparse.IsRelative(); + #ifndef _WIN32 + linkPath.Replace(L'\\', WCHAR_PATH_SEPARATOR); + #endif + } + } + } + + if (!linkPath.IsEmpty()) + { + #ifdef _WIN32 + linkPath.Replace(L'/', WCHAR_PATH_SEPARATOR); + #endif + + // rar5 uses "\??\" prefix for absolute links + if (linkPath.IsPrefixedBy(WSTRING_PATH_SEPARATOR L"??" WSTRING_PATH_SEPARATOR)) + { + isRelative = false; + linkPath.DeleteFrontal(4); + } + + for (;;) + // while (NName::IsAbsolutePath(linkPath)) + { + unsigned n = NName::GetRootPrefixSize(linkPath); + if (n == 0) + break; + isRelative = false; + linkPath.DeleteFrontal(n); + } + } + + if (!linkPath.IsEmpty() && !isRelative && _removePathParts.Size() != 0) + { + UStringVector pathParts; + SplitPathToParts(linkPath, pathParts); + bool badPrefix = false; + FOR_VECTOR (i, _removePathParts) + { + if (CompareFileNames(_removePathParts[i], pathParts[i]) != 0) + { + badPrefix = true; + break; + } + } + if (!badPrefix) + pathParts.DeleteFrontal(_removePathParts.Size()); + linkPath = MakePathFromParts(pathParts); + } + + #endif + + RINOK(Archive_GetItemBoolProp(archive, index, kpidEncrypted, _encrypted)); RINOK(GetUnpackSize()); + #ifdef SUPPORT_ALT_STREAMS + + if (!_ntOptions.AltStreams.Val && _item.IsAltStream) + return S_OK; + + #endif + + + UStringVector &pathParts = _item.PathParts; + if (_wildcardCensor) { - if (!_wildcardCensor->CheckPath(fullPath, !_fi.IsDir)) + if (!CensorNode_CheckPath(*_wildcardCensor, _item)) return S_OK; } - if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode) + #ifndef _SFX + if (_use_baseParentFolder_mode) { - if (_stdOutMode) + if (!pathParts.IsEmpty()) { - CMyComPtr<ISequentialOutStream> outStreamLoc = new CStdOutFileStream; - *outStream = outStreamLoc.Detach(); - return S_OK; + unsigned numRemovePathParts = 0; + + #ifdef SUPPORT_ALT_STREAMS + if (_pathMode == NExtract::NPathMode::kNoPathsAlt && _item.IsAltStream) + numRemovePathParts = pathParts.Size(); + else + #endif + if (_pathMode == NExtract::NPathMode::kNoPaths || + _pathMode == NExtract::NPathMode::kNoPathsAlt) + numRemovePathParts = pathParts.Size() - 1; + pathParts.DeleteFrontal(numRemovePathParts); + } + } + else + #endif + { + if (pathParts.IsEmpty()) + { + if (_item.IsDir) + return S_OK; + /* + #ifdef SUPPORT_ALT_STREAMS + if (!_item.IsAltStream) + #endif + return E_FAIL; + */ } + unsigned numRemovePathParts = 0; + + switch (_pathMode) + { + case NExtract::NPathMode::kFullPaths: + case NExtract::NPathMode::kCurPaths: + { + if (_removePathParts.IsEmpty()) + break; + bool badPrefix = false; + + if (pathParts.Size() < _removePathParts.Size()) + badPrefix = true; + else + { + if (pathParts.Size() == _removePathParts.Size()) + { + if (_removePartsForAltStreams) + { + #ifdef SUPPORT_ALT_STREAMS + if (!_item.IsAltStream) + #endif + badPrefix = true; + } + else + { + if (!_item.MainIsDir) + badPrefix = true; + } + } + + if (!badPrefix) + FOR_VECTOR (i, _removePathParts) + { + if (CompareFileNames(_removePathParts[i], pathParts[i]) != 0) + { + badPrefix = true; + break; + } + } + } + + if (badPrefix) + { + if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode) + return E_FAIL; + } + else + numRemovePathParts = _removePathParts.Size(); + break; + } + + case NExtract::NPathMode::kNoPaths: + { + if (!pathParts.IsEmpty()) + numRemovePathParts = pathParts.Size() - 1; + break; + } + case NExtract::NPathMode::kNoPathsAlt: + { + #ifdef SUPPORT_ALT_STREAMS + if (_item.IsAltStream) + numRemovePathParts = pathParts.Size(); + else + #endif + if (!pathParts.IsEmpty()) + numRemovePathParts = pathParts.Size() - 1; + break; + } + /* + case NExtract::NPathMode::kFullPaths: + case NExtract::NPathMode::kAbsPaths: + break; + */ + } + + pathParts.DeleteFrontal(numRemovePathParts); + } + + #ifndef _SFX + + if (ExtractToStreamCallback) + { + if (!GetProp) + { + GetProp_Spec = new CGetProp; + GetProp = GetProp_Spec; + } + GetProp_Spec->Arc = _arc; + GetProp_Spec->IndexInArc = index; + UString name = MakePathFromParts(pathParts); + + #ifdef SUPPORT_ALT_STREAMS + if (_item.IsAltStream) + { + if (!pathParts.IsEmpty() || (!_removePartsForAltStreams && _pathMode != NExtract::NPathMode::kNoPathsAlt)) + name += L':'; + name += _item.AltStreamName; + } + #endif + + return ExtractToStreamCallback->GetStream7(name, BoolToInt(_item.IsDir), outStream, askExtractMode, GetProp); + } + + #endif + + CMyComPtr<ISequentialOutStream> outStreamLoc; + +if (askExtractMode == NArchive::NExtract::NAskMode::kExtract && !_testMode) +{ + if (_stdOutMode) + { + outStreamLoc = new CStdOutFileStream; + } + else + { { NCOM::CPropVariant prop; RINOK(archive->GetProperty(index, kpidAttrib, &prop)); @@ -219,38 +975,44 @@ STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStre bool isAnti = false; RINOK(_arc->IsItemAnti(index, isAnti)); - UStringVector pathParts; - SplitPathToParts(fullPath, pathParts); + #ifdef SUPPORT_ALT_STREAMS + if (!_item.IsAltStream + || !pathParts.IsEmpty() + || !(_removePartsForAltStreams || _pathMode == NExtract::NPathMode::kNoPathsAlt)) + #endif + Correct_FsPath(_pathMode == NExtract::NPathMode::kAbsPaths, pathParts, _item.MainIsDir); + + #ifdef SUPPORT_ALT_STREAMS - if (pathParts.IsEmpty()) - return E_FAIL; - int numRemovePathParts = 0; - switch(_pathMode) + if (_item.IsAltStream) { - case NExtract::NPathMode::kFullPathnames: - break; - case NExtract::NPathMode::kCurrentPathnames: - { - numRemovePathParts = _removePathParts.Size(); - if (pathParts.Size() <= numRemovePathParts) - return E_FAIL; - for (int i = 0; i < numRemovePathParts; i++) - if (_removePathParts[i].CompareNoCase(pathParts[i]) != 0) - return E_FAIL; - break; - } - case NExtract::NPathMode::kNoPathnames: + UString s = _item.AltStreamName; + Correct_AltStream_Name(s); + bool needColon = true; + + if (pathParts.IsEmpty()) { - numRemovePathParts = pathParts.Size() - 1; - break; + pathParts.AddNew(); + if (_removePartsForAltStreams || _pathMode == NExtract::NPathMode::kNoPathsAlt) + needColon = false; } + else if (_pathMode == NExtract::NPathMode::kAbsPaths && + NWildcard::GetNumPrefixParts_if_DrivePath(pathParts) == pathParts.Size()) + pathParts.AddNew(); + + UString &name = pathParts.Back(); + if (needColon) + name += (wchar_t)(_ntOptions.ReplaceColonForAltStream ? L'_' : L':'); + name += s; } - pathParts.Delete(0, numRemovePathParts); - MakeCorrectPath(pathParts); - UString processedPath = MakePathNameFromParts(pathParts); + + #endif + + UString processedPath = MakePathFromParts(pathParts); + if (!isAnti) { - if (!_fi.IsDir) + if (!_item.IsDir) { if (!pathParts.IsEmpty()) pathParts.DeleteBack(); @@ -260,74 +1022,103 @@ STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStre { FString fullPathNew; CreateComplexDirectory(pathParts, fullPathNew); - if (_fi.IsDir) - NFile::NDirectory::SetDirTime(fullPathNew, + if (_item.IsDir) + { + _extractedFolderPaths.Add(fullPathNew); + _extractedFolderIndices.Add(index); + SetDirTime(fullPathNew, (WriteCTime && _fi.CTimeDefined) ? &_fi.CTime : NULL, (WriteATime && _fi.ATimeDefined) ? &_fi.ATime : NULL, (WriteMTime && _fi.MTimeDefined) ? &_fi.MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL)); + } } } - FString fullProcessedPath = _directoryPath + us2fs(processedPath); + FString fullProcessedPath = us2fs(processedPath); + if (_pathMode != NExtract::NPathMode::kAbsPaths + || !NName::IsAbsolutePath(processedPath)) + { + fullProcessedPath = MakePath_from_2_Parts(_dirPathPrefix, fullProcessedPath); + } - if (_fi.IsDir) + #ifdef SUPPORT_ALT_STREAMS + + if (_item.IsAltStream && _item.ParentIndex != (UInt32)(Int32)-1) + { + CIndexToPathPair pair(_item.ParentIndex); + int renIndex = _renamedFiles.FindInSorted(pair); + if (renIndex >= 0) + { + const CIndexToPathPair &pair = _renamedFiles[renIndex]; + fullProcessedPath = pair.Path; + fullProcessedPath += (FChar)':'; + UString s = _item.AltStreamName; + Correct_AltStream_Name(s); + fullProcessedPath += us2fs(s); + } + } + + #endif + + bool isRenamed = false; + + if (_item.IsDir) { _diskFilePath = fullProcessedPath; if (isAnti) - NFile::NDirectory::MyRemoveDirectory(_diskFilePath); - return S_OK; + RemoveDir(_diskFilePath); + #ifdef SUPPORT_LINKS + if (linkPath.IsEmpty()) + #endif + return S_OK; } - - if (!_isSplit) + else if (!_isSplit) { - NFile::NFind::CFileInfo fileInfo; + + // ----- Is file (not split) ----- + NFind::CFileInfo fileInfo; if (fileInfo.Find(fullProcessedPath)) { - switch(_overwriteMode) + switch (_overwriteMode) { - case NExtract::NOverwriteMode::kSkipExisting: + case NExtract::NOverwriteMode::kSkip: return S_OK; - case NExtract::NOverwriteMode::kAskBefore: + case NExtract::NOverwriteMode::kAsk: { - Int32 overwiteResult; + int slashPos = fullProcessedPath.ReverseFind_PathSepar(); + FString realFullProcessedPath = fullProcessedPath.Left(slashPos + 1) + fileInfo.Name; + + Int32 overwriteResult; RINOK(_extractCallback2->AskOverwrite( - fs2us(fullProcessedPath), &fileInfo.MTime, &fileInfo.Size, fullPath, + fs2us(realFullProcessedPath), &fileInfo.MTime, &fileInfo.Size, _item.Path, _fi.MTimeDefined ? &_fi.MTime : NULL, _curSizeDefined ? &_curSize : NULL, - &overwiteResult)) + &overwriteResult)) - switch(overwiteResult) + switch (overwriteResult) { - case NOverwriteAnswer::kCancel: - return E_ABORT; - case NOverwriteAnswer::kNo: - return S_OK; - case NOverwriteAnswer::kNoToAll: - _overwriteMode = NExtract::NOverwriteMode::kSkipExisting; - return S_OK; - case NOverwriteAnswer::kYesToAll: - _overwriteMode = NExtract::NOverwriteMode::kWithoutPrompt; - break; - case NOverwriteAnswer::kYes: - break; - case NOverwriteAnswer::kAutoRename: - _overwriteMode = NExtract::NOverwriteMode::kAutoRename; - break; + case NOverwriteAnswer::kCancel: return E_ABORT; + case NOverwriteAnswer::kNo: return S_OK; + case NOverwriteAnswer::kNoToAll: _overwriteMode = NExtract::NOverwriteMode::kSkip; return S_OK; + case NOverwriteAnswer::kYes: break; + case NOverwriteAnswer::kYesToAll: _overwriteMode = NExtract::NOverwriteMode::kOverwrite; break; + case NOverwriteAnswer::kAutoRename: _overwriteMode = NExtract::NOverwriteMode::kRename; break; default: return E_FAIL; } } } - if (_overwriteMode == NExtract::NOverwriteMode::kAutoRename) + if (_overwriteMode == NExtract::NOverwriteMode::kRename) { if (!AutoRenamePath(fullProcessedPath)) { RINOK(SendMessageError(kCantAutoRename, fullProcessedPath)); return E_FAIL; } + isRenamed = true; } - else if (_overwriteMode == NExtract::NOverwriteMode::kAutoRenameExisting) + else if (_overwriteMode == NExtract::NOverwriteMode::kRenameExisting) { FString existPath = fullProcessedPath; if (!AutoRenamePath(existPath)) @@ -335,65 +1126,290 @@ STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStre RINOK(SendMessageError(kCantAutoRename, fullProcessedPath)); return E_FAIL; } - if (!NFile::NDirectory::MyMoveFile(fullProcessedPath, existPath)) + // MyMoveFile can raname folders. So it's OK to use it for folders too + if (!MyMoveFile(fullProcessedPath, existPath)) { - RINOK(SendMessageError(kCantRenameFile, fullProcessedPath)); + RINOK(SendMessageError2(kCantRenameFile, existPath, fullProcessedPath)); return E_FAIL; } } else - if (!NFile::NDirectory::DeleteFileAlways(fullProcessedPath)) + { + if (fileInfo.IsDir()) { - RINOK(SendMessageError(kCantDeleteOutputFile, fullProcessedPath)); - return S_OK; - // return E_FAIL; + // do we need to delete all files in folder? + if (!RemoveDir(fullProcessedPath)) + { + RINOK(SendMessageError_with_LastError(kCantDeleteOutputDir, fullProcessedPath)); + return S_OK; + } } + else + { + bool needDelete = true; + if (needDelete) + { + if (!DeleteFileAlways(fullProcessedPath)) + { + RINOK(SendMessageError_with_LastError(kCantDeleteOutputFile, fullProcessedPath)); + return S_OK; + // return E_FAIL; + } + } + } + } } + else // not Find(fullProcessedPath) + { + // we need to clear READ-ONLY of parent before creating alt stream + #if defined(_WIN32) && !defined(UNDER_CE) + int colonPos = NName::FindAltStreamColon(fullProcessedPath); + if (colonPos >= 0 && fullProcessedPath[(unsigned)colonPos + 1] != 0) + { + FString parentFsPath = fullProcessedPath; + parentFsPath.DeleteFrom(colonPos); + NFind::CFileInfo parentFi; + if (parentFi.Find(parentFsPath)) + { + if (parentFi.IsReadOnly()) + SetFileAttrib(parentFsPath, parentFi.Attrib & ~FILE_ATTRIBUTE_READONLY); + } + } + #endif + } + // ----- END of code for Is file (not split) ----- + } + _diskFilePath = fullProcessedPath; + + if (!isAnti) { - _outFileStreamSpec = new COutFileStream; - CMyComPtr<ISequentialOutStream> outStreamLoc(_outFileStreamSpec); - if (!_outFileStreamSpec->Open(fullProcessedPath, _isSplit ? OPEN_ALWAYS: CREATE_ALWAYS)) + #ifdef SUPPORT_LINKS + + if (!linkPath.IsEmpty()) { - // if (::GetLastError() != ERROR_FILE_EXISTS || !isSplit) + #ifndef UNDER_CE + + UString relatPath; + if (isRelative) + relatPath = GetDirPrefixOf(_item.Path); + relatPath += linkPath; + + if (!IsSafePath(relatPath)) { - RINOK(SendMessageError("can not open output file ", fullProcessedPath)); - return S_OK; + RINOK(SendMessageError("Dangerous link path was ignored", us2fs(relatPath))); + } + else + { + FString existPath; + if (isHardLink /* || isCopyLink */ || !isRelative) + { + if (!NName::GetFullPath(_dirPathPrefix_Full, us2fs(relatPath), existPath)) + { + RINOK(SendMessageError("Incorrect path", us2fs(relatPath))); + } + } + else + { + existPath = us2fs(linkPath); + } + + if (!existPath.IsEmpty()) + { + if (isHardLink /* || isCopyLink */) + { + // if (isHardLink) + { + if (!MyCreateHardLink(fullProcessedPath, existPath)) + { + RINOK(SendMessageError2("Can not create hard link", fullProcessedPath, existPath)); + // return S_OK; + } + } + /* + else + { + NFind::CFileInfo fi; + if (!fi.Find(existPath)) + { + RINOK(SendMessageError2("Can not find the file for copying", existPath, fullProcessedPath)); + } + else + { + if (_curSizeDefined && _curSize == fi.Size) + _CopyFile_Path = existPath; + else + { + RINOK(SendMessageError2("File size collision for file copying", existPath, fullProcessedPath)); + } + + // RINOK(MyCopyFile(existPath, fullProcessedPath)); + } + } + */ + } + else if (_ntOptions.SymLinks.Val) + { + // bool isSymLink = true; // = false for junction + if (_item.IsDir && !isRelative) + { + // if it's before Vista we use Junction Point + // isJunction = true; + // convertToAbs = true; + } + + CByteBuffer data; + if (FillLinkData(data, fs2us(existPath), !isJunction)) + { + CReparseAttr attr; + if (!attr.Parse(data, data.Size())) + { + RINOK(SendMessageError("Internal error for symbolic link file", us2fs(_item.Path))); + // return E_FAIL; + } + else + if (!NFile::NIO::SetReparseData(fullProcessedPath, _item.IsDir, data, (DWORD)data.Size())) + { + RINOK(SendMessageError_with_LastError("Can not create symbolic link", fullProcessedPath)); + } + } + } + } } + + #endif } - if (_isSplit) + + if (linkPath.IsEmpty() /* || !_CopyFile_Path.IsEmpty() */) + #endif // SUPPORT_LINKS { - RINOK(_outFileStreamSpec->Seek(_position, STREAM_SEEK_SET, NULL)); + bool needWriteFile = true; + + #ifdef SUPPORT_LINKS + if (!_hardLinks.IDs.IsEmpty() && !_item.IsAltStream) + { + CHardLinkNode h; + bool defined; + RINOK(Archive_Get_HardLinkNode(archive, index, h, defined)); + if (defined) + { + { + int linkIndex = _hardLinks.IDs.FindInSorted2(h); + if (linkIndex >= 0) + { + FString &hl = _hardLinks.Links[linkIndex]; + if (hl.IsEmpty()) + hl = fullProcessedPath; + else + { + if (!MyCreateHardLink(fullProcessedPath, hl)) + { + RINOK(SendMessageError2("Can not create hard link", fullProcessedPath, hl)); + return S_OK; + } + needWriteFile = false; + } + } + } + } + } + #endif + + if (needWriteFile) + { + _outFileStreamSpec = new COutFileStream; + CMyComPtr<ISequentialOutStream> outStreamLoc(_outFileStreamSpec); + if (!_outFileStreamSpec->Open(fullProcessedPath, _isSplit ? OPEN_ALWAYS: CREATE_ALWAYS)) + { + // if (::GetLastError() != ERROR_FILE_EXISTS || !isSplit) + { + RINOK(SendMessageError_with_LastError("Can not open output file", fullProcessedPath)); + return S_OK; + } + } + + + #ifdef SUPPORT_ALT_STREAMS + if (isRenamed && !_item.IsAltStream) + { + CIndexToPathPair pair(index, fullProcessedPath); + unsigned oldSize = _renamedFiles.Size(); + unsigned insertIndex = _renamedFiles.AddToUniqueSorted(pair); + if (oldSize == _renamedFiles.Size()) + _renamedFiles[insertIndex].Path = fullProcessedPath; + } + #endif + + if (_isSplit) + { + RINOK(_outFileStreamSpec->Seek(_position, STREAM_SEEK_SET, NULL)); + } + + _outFileStream = outStreamLoc; + } } - _outFileStream = outStreamLoc; - *outStream = outStreamLoc.Detach(); } - _diskFilePath = fullProcessedPath; + + outStreamLoc = _outFileStream; } - else +} + + #ifndef _SFX + + if (_hashStream) { - *outStream = NULL; + if (askExtractMode == NArchive::NExtract::NAskMode::kExtract || + askExtractMode == NArchive::NExtract::NAskMode::kTest) + { + _hashStreamSpec->SetStream(outStreamLoc); + outStreamLoc = _hashStream; + _hashStreamSpec->Init(true); + _hashStreamWasUsed = true; + } } - if (_crcMode) + + #endif + + + if (outStreamLoc) { - _crcStreamSpec = new COutStreamWithCRC; - _crcStream = _crcStreamSpec; - CMyComPtr<ISequentialOutStream> crcStream = _crcStreamSpec; - _crcStreamSpec->SetStream(*outStream); - if (*outStream) - (*outStream)->Release(); - *outStream = crcStream.Detach(); - _crcStreamSpec->Init(true); + /* + #ifdef SUPPORT_LINKS + + if (!_CopyFile_Path.IsEmpty()) + { + RINOK(PrepareOperation(askExtractMode)); + RINOK(MyCopyFile(outStreamLoc)); + return SetOperationResult(NArchive::NExtract::NOperationResult::kOK); + } + + if (isCopyLink && _testMode) + return S_OK; + + #endif + */ + + *outStream = outStreamLoc.Detach(); } + return S_OK; + COM_TRY_END } + STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode) { COM_TRY_BEGIN + + #ifndef _SFX + if (ExtractToStreamCallback) + return ExtractToStreamCallback->PrepareOperation7(askExtractMode); + #endif + _extractMode = false; + switch (askExtractMode) { case NArchive::NExtract::NAskMode::kExtract: @@ -403,32 +1419,42 @@ STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode) _extractMode = true; break; }; - return _extractCallback2->PrepareOperation(_filePath, _fi.IsDir, + + return _extractCallback2->PrepareOperation(_item.Path, BoolToInt(_item.IsDir), askExtractMode, _isSplit ? &_position: 0); + COM_TRY_END } -STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult) + +STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 opRes) { COM_TRY_BEGIN - switch(operationResult) - { - case NArchive::NExtract::NOperationResult::kOK: - case NArchive::NExtract::NOperationResult::kUnSupportedMethod: - case NArchive::NExtract::NOperationResult::kCRCError: - case NArchive::NExtract::NOperationResult::kDataError: - break; - default: - _outFileStream.Release(); - return E_FAIL; - } - if (_crcStream) + + #ifndef _SFX + if (ExtractToStreamCallback) + return ExtractToStreamCallback->SetOperationResult7(opRes, BoolToInt(_encrypted)); + #endif + + #ifndef _SFX + + if (_hashStreamWasUsed) { - CrcSum += _crcStreamSpec->GetCRC(); - _curSize = _crcStreamSpec->GetSize(); + _hashStreamSpec->_hash->Final(_item.IsDir, + #ifdef SUPPORT_ALT_STREAMS + _item.IsAltStream + #else + false + #endif + , _item.Path); + _curSize = _hashStreamSpec->GetSize(); _curSizeDefined = true; - _crcStream.Release(); + _hashStreamSpec->ReleaseStream(); + _hashStreamWasUsed = false; } + + #endif + if (_outFileStream) { _outFileStreamSpec->SetTime( @@ -440,36 +1466,92 @@ STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult) RINOK(_outFileStreamSpec->Close()); _outFileStream.Release(); } + + #ifdef _USE_SECURITY_CODE + if (!_stdOutMode && _extractMode && _ntOptions.NtSecurity.Val && _arc->GetRawProps) + { + const void *data; + UInt32 dataSize; + UInt32 propType; + _arc->GetRawProps->GetRawProp(_index, kpidNtSecure, &data, &dataSize, &propType); + if (dataSize != 0) + { + if (propType != NPropDataType::kRaw) + return E_FAIL; + if (CheckNtSecure((const Byte *)data, dataSize)) + { + SECURITY_INFORMATION securInfo = DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION; + if (_saclEnabled) + securInfo |= SACL_SECURITY_INFORMATION; + ::SetFileSecurityW(fs2us(_diskFilePath), securInfo, (PSECURITY_DESCRIPTOR)(void *)data); + } + } + } + #endif + if (!_curSizeDefined) GetUnpackSize(); + if (_curSizeDefined) - UnpackSize += _curSize; - if (_fi.IsDir) + { + #ifdef SUPPORT_ALT_STREAMS + if (_item.IsAltStream) + AltStreams_UnpackSize += _curSize; + else + #endif + UnpackSize += _curSize; + } + + if (_item.IsDir) NumFolders++; + #ifdef SUPPORT_ALT_STREAMS + else if (_item.IsAltStream) + NumAltStreams++; + #endif else NumFiles++; - if (_extractMode && _fi.AttribDefined) - NFile::NDirectory::MySetFileAttributes(_diskFilePath, _fi.Attrib); - RINOK(_extractCallback2->SetOperationResult(operationResult, _encrypted)); + if (!_stdOutMode && _extractMode && _fi.AttribDefined) + SetFileAttrib(_diskFilePath, _fi.Attrib); + + RINOK(_extractCallback2->SetOperationResult(opRes, BoolToInt(_encrypted))); + return S_OK; + COM_TRY_END } -/* -STDMETHODIMP CArchiveExtractCallback::GetInStream( - const wchar_t *name, ISequentialInStream **inStream) +STDMETHODIMP CArchiveExtractCallback::ReportExtractResult(UInt32 indexType, UInt32 index, Int32 opRes) { - COM_TRY_BEGIN - CInFileStream *inFile = new CInFileStream; - CMyComPtr<ISequentialInStream> inStreamTemp = inFile; - if (!inFile->Open(_srcDirectoryPrefix + name)) - return ::GetLastError(); - *inStream = inStreamTemp.Detach(); + if (_folderArchiveExtractCallback2) + { + bool isEncrypted = false; + wchar_t temp[16]; + UString s2; + const wchar_t *s = NULL; + + if (indexType == NArchive::NEventIndexType::kInArcIndex && index != (UInt32)(Int32)-1) + { + CReadArcItem item; + RINOK(_arc->GetItem(index, item)); + s2 = item.Path; + s = s2; + RINOK(Archive_GetItemBoolProp(_arc->Archive, index, kpidEncrypted, isEncrypted)); + } + else + { + temp[0] = '#'; + ConvertUInt32ToString(index, temp + 1); + s = temp; + // if (indexType == NArchive::NEventIndexType::kBlockIndex) {} + } + + return _folderArchiveExtractCallback2->ReportExtractResult(opRes, isEncrypted, s); + } + return S_OK; - COM_TRY_END } -*/ + STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password) { @@ -482,3 +1564,73 @@ STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password) return _cryptoGetTextPassword->CryptoGetTextPassword(password); COM_TRY_END } + + +struct CExtrRefSortPair +{ + unsigned Len; + unsigned Index; + + int Compare(const CExtrRefSortPair &a) const; +}; + +#define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; } + +int CExtrRefSortPair::Compare(const CExtrRefSortPair &a) const +{ + RINOZ(-MyCompare(Len, a.Len)); + return MyCompare(Index, a.Index); +} + +static unsigned GetNumSlashes(const FChar *s) +{ + for (unsigned numSlashes = 0;;) + { + FChar c = *s++; + if (c == 0) + return numSlashes; + if (IS_PATH_SEPAR(c)) + numSlashes++; + } +} + +HRESULT CArchiveExtractCallback::SetDirsTimes() +{ + CRecordVector<CExtrRefSortPair> pairs; + pairs.ClearAndSetSize(_extractedFolderPaths.Size()); + unsigned i; + + for (i = 0; i < _extractedFolderPaths.Size(); i++) + { + CExtrRefSortPair &pair = pairs[i]; + pair.Index = i; + pair.Len = GetNumSlashes(_extractedFolderPaths[i]); + } + + pairs.Sort2(); + + for (i = 0; i < pairs.Size(); i++) + { + int pairIndex = pairs[i].Index; + int index = _extractedFolderIndices[pairIndex]; + + FILETIME CTime; + FILETIME ATime; + FILETIME MTime; + + bool CTimeDefined; + bool ATimeDefined; + bool MTimeDefined; + + RINOK(GetTime(index, kpidCTime, CTime, CTimeDefined)); + RINOK(GetTime(index, kpidATime, ATime, ATimeDefined)); + RINOK(GetTime(index, kpidMTime, MTime, MTimeDefined)); + + // printf("\n%S", _extractedFolderPaths[pairIndex]); + SetDirTime(_extractedFolderPaths[pairIndex], + (WriteCTime && CTimeDefined) ? &CTime : NULL, + (WriteATime && ATimeDefined) ? &ATime : NULL, + (WriteMTime && MTimeDefined) ? &MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL)); + } + return S_OK; +} diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveExtractCallback.h b/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveExtractCallback.h index cee7c5b6180..7d4155fe2e7 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveExtractCallback.h +++ b/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveExtractCallback.h @@ -3,8 +3,8 @@ #ifndef __ARCHIVE_EXTRACT_CALLBACK_H #define __ARCHIVE_EXTRACT_CALLBACK_H -#include "Common/MyCom.h" -#include "Common/Wildcard.h" +#include "../../../Common/MyCom.h" +#include "../../../Common/Wildcard.h" #include "../../IPassword.h" @@ -13,30 +13,167 @@ #include "../../Archive/IArchive.h" -#include "../../Archive/Common/OutStreamWithCRC.h" - #include "ExtractMode.h" #include "IFileExtractCallback.h" #include "OpenArchive.h" +#include "HashCalc.h" + +#ifndef _SFX + +class COutStreamWithHash: + public ISequentialOutStream, + public CMyUnknownImp +{ + CMyComPtr<ISequentialOutStream> _stream; + UInt64 _size; + bool _calculate; +public: + IHashCalc *_hash; + + MY_UNKNOWN_IMP + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); + void SetStream(ISequentialOutStream *stream) { _stream = stream; } + void ReleaseStream() { _stream.Release(); } + void Init(bool calculate = true) + { + InitCRC(); + _size = 0; + _calculate = calculate; + } + void EnableCalc(bool calculate) { _calculate = calculate; } + void InitCRC() { _hash->InitForNewFile(); } + UInt64 GetSize() const { return _size; } +}; + +#endif + +struct CExtractNtOptions +{ + CBoolPair NtSecurity; + CBoolPair SymLinks; + CBoolPair HardLinks; + CBoolPair AltStreams; + bool ReplaceColonForAltStream; + bool WriteToAltStreamIfColon; + + CExtractNtOptions(): + ReplaceColonForAltStream(false), + WriteToAltStreamIfColon(false) + { + SymLinks.Val = true; + HardLinks.Val = true; + AltStreams.Val = true; + } +}; + +#ifndef _SFX + +class CGetProp: + public IGetProp, + public CMyUnknownImp +{ +public: + const CArc *Arc; + UInt32 IndexInArc; + // UString Name; // relative path + + MY_UNKNOWN_IMP1(IGetProp) + INTERFACE_IGetProp(;) +}; + +#endif + +#ifndef _SFX +#ifndef UNDER_CE + +#define SUPPORT_LINKS + +#endif +#endif + + +#ifdef SUPPORT_LINKS + +struct CHardLinkNode +{ + UInt64 StreamId; + UInt64 INode; + + int Compare(const CHardLinkNode &a) const; +}; + +class CHardLinks +{ +public: + CRecordVector<CHardLinkNode> IDs; + CObjectVector<FString> Links; + + void Clear() + { + IDs.Clear(); + Links.Clear(); + } + + void PrepareLinks() + { + while (Links.Size() < IDs.Size()) + Links.AddNew(); + } +}; + +#endif + +#ifdef SUPPORT_ALT_STREAMS + +struct CIndexToPathPair +{ + UInt32 Index; + FString Path; + + CIndexToPathPair(UInt32 index): Index(index) {} + CIndexToPathPair(UInt32 index, const FString &path): Index(index), Path(path) {} + + int Compare(const CIndexToPathPair &pair) const + { + return MyCompare(Index, pair.Index); + } +}; + +#endif + class CArchiveExtractCallback: public IArchiveExtractCallback, - // public IArchiveVolumeExtractCallback, + public IArchiveExtractCallbackMessage, public ICryptoGetTextPassword, public ICompressProgressInfo, public CMyUnknownImp { const CArc *_arc; - const NWildcard::CCensorNode *_wildcardCensor; + CExtractNtOptions _ntOptions; + + const NWildcard::CCensorNode *_wildcardCensor; // we need wildcard for single pass mode (stdin) CMyComPtr<IFolderArchiveExtractCallback> _extractCallback2; CMyComPtr<ICompressProgressInfo> _compressProgress; CMyComPtr<ICryptoGetTextPassword> _cryptoGetTextPassword; - FString _directoryPath; + CMyComPtr<IArchiveExtractCallbackMessage> _callbackMessage; + CMyComPtr<IFolderArchiveExtractCallback2> _folderArchiveExtractCallback2; + + FString _dirPathPrefix; + FString _dirPathPrefix_Full; NExtract::NPathMode::EEnum _pathMode; NExtract::NOverwriteMode::EEnum _overwriteMode; + #ifndef _SFX + + CMyComPtr<IFolderExtractToStreamCallback> ExtractToStreamCallback; + CGetProp *GetProp_Spec; + CMyComPtr<IGetProp> GetProp; + + #endif + + CReadArcItem _item; FString _diskFilePath; - UString _filePath; UInt64 _position; bool _isSplit; @@ -59,8 +196,6 @@ class CArchiveExtractCallback: bool ATimeDefined; bool MTimeDefined; bool AttribDefined; - - bool IsDir; } _fi; UInt32 _index; @@ -69,25 +204,46 @@ class CArchiveExtractCallback: COutFileStream *_outFileStreamSpec; CMyComPtr<ISequentialOutStream> _outFileStream; - COutStreamWithCRC *_crcStreamSpec; - CMyComPtr<ISequentialOutStream> _crcStream; + #ifndef _SFX + + COutStreamWithHash *_hashStreamSpec; + CMyComPtr<ISequentialOutStream> _hashStream; + bool _hashStreamWasUsed; + + #endif + bool _removePartsForAltStreams; UStringVector _removePathParts; + + #ifndef _SFX + bool _use_baseParentFolder_mode; + UInt32 _baseParentFolder; + #endif bool _stdOutMode; bool _testMode; - bool _crcMode; bool _multiArchives; CMyComPtr<ICompressProgressInfo> _localProgress; UInt64 _packTotal; - UInt64 _unpTotal; + + UInt64 _progressTotal; + bool _progressTotal_Defined; + + FStringVector _extractedFolderPaths; + CRecordVector<UInt32> _extractedFolderIndices; + + #if defined(_WIN32) && !defined(UNDER_CE) && !defined(_SFX) + bool _saclEnabled; + #endif void CreateComplexDirectory(const UStringVector &dirPathParts, FString &fullPath); HRESULT GetTime(int index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined); HRESULT GetUnpackSize(); HRESULT SendMessageError(const char *message, const FString &path); + HRESULT SendMessageError_with_LastError(const char *message, const FString &path); + HRESULT SendMessageError2(const char *message, const FString &path1, const FString &path2); public: @@ -95,30 +251,20 @@ public: UInt64 NumFolders; UInt64 NumFiles; + UInt64 NumAltStreams; UInt64 UnpackSize; - UInt32 CrcSum; + UInt64 AltStreams_UnpackSize; - MY_UNKNOWN_IMP2(ICryptoGetTextPassword, ICompressProgressInfo) - // COM_INTERFACE_ENTRY(IArchiveVolumeExtractCallback) + MY_UNKNOWN_IMP3(IArchiveExtractCallbackMessage, ICryptoGetTextPassword, ICompressProgressInfo) INTERFACE_IArchiveExtractCallback(;) + INTERFACE_IArchiveExtractCallbackMessage(;) STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); - // IArchiveVolumeExtractCallback - // STDMETHOD(GetInStream)(const wchar_t *name, ISequentialInStream **inStream); - STDMETHOD(CryptoGetTextPassword)(BSTR *password); - CArchiveExtractCallback(): - WriteCTime(true), - WriteATime(true), - WriteMTime(true), - _multiArchives(false) - { - LocalProgressSpec = new CLocalProgress(); - _localProgress = LocalProgressSpec; - } + CArchiveExtractCallback(); void InitForMulti(bool multiArchives, NExtract::NPathMode::EEnum pathMode, @@ -127,19 +273,66 @@ public: _multiArchives = multiArchives; _pathMode = pathMode; _overwriteMode = overwriteMode; - NumFolders = NumFiles = UnpackSize = 0; - CrcSum = 0; + NumFolders = NumFiles = NumAltStreams = UnpackSize = AltStreams_UnpackSize = 0; + } + + #ifndef _SFX + + void SetHashMethods(IHashCalc *hash) + { + if (!hash) + return; + _hashStreamSpec = new COutStreamWithHash; + _hashStream = _hashStreamSpec; + _hashStreamSpec->_hash = hash; } + #endif + void Init( + const CExtractNtOptions &ntOptions, const NWildcard::CCensorNode *wildcardCensor, const CArc *arc, IFolderArchiveExtractCallback *extractCallback2, - bool stdOutMode, bool testMode, bool crcMode, + bool stdOutMode, bool testMode, const FString &directoryPath, - const UStringVector &removePathParts, + const UStringVector &removePathParts, bool removePartsForAltStreams, UInt64 packSize); + + #ifdef SUPPORT_LINKS + +private: + CHardLinks _hardLinks; + UString linkPath; + + // FString _CopyFile_Path; + // HRESULT MyCopyFile(ISequentialOutStream *outStream); + +public: + // call PrepareHardLinks() after Init() + HRESULT PrepareHardLinks(const CRecordVector<UInt32> *realIndices); // NULL means all items + + #endif + + + #ifdef SUPPORT_ALT_STREAMS + CObjectVector<CIndexToPathPair> _renamedFiles; + #endif + + // call it after Init() + + #ifndef _SFX + void SetBaseParentFolderIndex(UInt32 indexInArc) + { + _baseParentFolder = indexInArc; + _use_baseParentFolder_mode = true; + } + #endif + + HRESULT SetDirsTimes(); }; +bool CensorNode_CheckPath(const NWildcard::CCensorNode &node, const CReadArcItem &item); + #endif diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveName.cpp b/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveName.cpp index 17947dcef43..06d2ab80895 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveName.cpp +++ b/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveName.cpp @@ -2,53 +2,77 @@ #include "StdAfx.h" -#include "Windows/FileDir.h" -#include "Windows/FileFind.h" +#include "../../../Windows/FileDir.h" +#include "../../../Windows/FileName.h" #include "ExtractingFilePath.h" +#include "ArchiveName.h" using namespace NWindows; +using namespace NFile; -static FString CreateArchiveName2(const FString &srcName, bool fromPrev, bool keepName) +UString CreateArchiveName(const NFind::CFileInfo &fi, bool keepName) +{ + FString resultName = fi.Name; + if (!fi.IsDir() && !keepName) + { + int dotPos = resultName.ReverseFind_Dot(); + if (dotPos > 0) + { + FString archiveName2 = resultName.Left(dotPos); + if (archiveName2.ReverseFind_Dot() < 0) + resultName = archiveName2; + } + } + return Get_Correct_FsFile_Name(fs2us(resultName)); +} + +static FString CreateArchiveName2(const FString &path, bool fromPrev, bool keepName) { FString resultName = FTEXT("Archive"); if (fromPrev) { FString dirPrefix; - if (NFile::NDirectory::GetOnlyDirPrefix(srcName, dirPrefix)) + if (NDir::GetOnlyDirPrefix(path, dirPrefix)) { - if (dirPrefix.Length() > 0) - if (dirPrefix.Back() == FCHAR_PATH_SEPARATOR) + if (!dirPrefix.IsEmpty() && IsPathSepar(dirPrefix.Back())) + { + #if defined(_WIN32) && !defined(UNDER_CE) + if (NName::IsDriveRootPath_SuperAllowed(dirPrefix)) + resultName = dirPrefix[dirPrefix.Len() - 3]; // only letter + else + #endif { dirPrefix.DeleteBack(); - NFile::NFind::CFileInfo fileInfo; - if (fileInfo.Find(dirPrefix)) - resultName = fileInfo.Name; + NFind::CFileInfo fi; + if (fi.Find(dirPrefix)) + resultName = fi.Name; } + } } } else { - NFile::NFind::CFileInfo fileInfo; - if (!fileInfo.Find(srcName)) - // return resultName; - return srcName; - resultName = fileInfo.Name; - if (!fileInfo.IsDir() && !keepName) + NFind::CFileInfo fi; + if (fi.Find(path)) { - int dotPos = resultName.ReverseFind('.'); - if (dotPos > 0) + resultName = fi.Name; + if (!fi.IsDir() && !keepName) { - FString archiveName2 = resultName.Left(dotPos); - if (archiveName2.ReverseFind(FTEXT('.')) < 0) - resultName = archiveName2; + int dotPos = resultName.ReverseFind_Dot(); + if (dotPos > 0) + { + FString name2 = resultName.Left(dotPos); + if (name2.ReverseFind_Dot() < 0) + resultName = name2; + } } } } return resultName; } -UString CreateArchiveName(const UString &srcName, bool fromPrev, bool keepName) +UString CreateArchiveName(const UString &path, bool fromPrev, bool keepName) { - return GetCorrectFsPath(fs2us(CreateArchiveName2(us2fs(srcName), fromPrev, keepName))); + return Get_Correct_FsFile_Name(fs2us(CreateArchiveName2(us2fs(path), fromPrev, keepName))); } diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveName.h b/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveName.h index 9513fb2ba8b..ec2f1e8d1d9 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveName.h +++ b/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveName.h @@ -1,10 +1,13 @@ // ArchiveName.h -#ifndef __ARCHIVENAME_H -#define __ARCHIVENAME_H +#ifndef __ARCHIVE_NAME_H +#define __ARCHIVE_NAME_H -#include "Common/MyString.h" +#include "../../../Common/MyString.h" -UString CreateArchiveName(const UString &srcName, bool fromPrev, bool keepName); +#include "../../../Windows/FileFind.h" + +UString CreateArchiveName(const UString &path, bool fromPrev, bool keepName); +UString CreateArchiveName(const NWindows::NFile::NFind::CFileInfo &fileInfo, bool keepName); #endif diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp b/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp index be20940c033..359835d3c2c 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp +++ b/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp @@ -2,9 +2,10 @@ #include "StdAfx.h" -#include "Common/ComTry.h" +#include "../../../Common/ComTry.h" -#include "Windows/PropVariant.h" +#include "../../../Windows/FileName.h" +#include "../../../Windows/PropVariant.h" #include "../../Common/FileStreams.h" @@ -39,12 +40,12 @@ STDMETHODIMP COpenCallbackImp::GetProperty(PROPID propID, PROPVARIANT *value) COM_TRY_BEGIN NCOM::CPropVariant prop; if (_subArchiveMode) - switch(propID) + switch (propID) { case kpidName: prop = _subArchiveName; break; } else - switch(propID) + switch (propID) { case kpidName: prop = _fileInfo.Name; break; case kpidIsDir: prop = _fileInfo.IsDir(); break; @@ -59,41 +60,54 @@ STDMETHODIMP COpenCallbackImp::GetProperty(PROPID propID, PROPVARIANT *value) COM_TRY_END } -int COpenCallbackImp::FindName(const UString &name) -{ - for (int i = 0; i < FileNames.Size(); i++) - if (name.CompareNoCase(FileNames[i]) == 0) - return i; - return -1; -} - struct CInFileStreamVol: public CInFileStream { - UString Name; + int FileNameIndex; COpenCallbackImp *OpenCallbackImp; CMyComPtr<IArchiveOpenCallback> OpenCallbackRef; + ~CInFileStreamVol() { if (OpenCallbackRef) - { - int index = OpenCallbackImp->FindName(Name); - if (index >= 0) - OpenCallbackImp->FileNames.Delete(index); - } + OpenCallbackImp->FileNames_WasUsed[FileNameIndex] = false; } }; + +// from ArchiveExtractCallback.cpp +bool IsSafePath(const UString &path); + STDMETHODIMP COpenCallbackImp::GetStream(const wchar_t *name, IInStream **inStream) { COM_TRY_BEGIN + *inStream = NULL; + if (_subArchiveMode) return S_FALSE; if (Callback) { RINOK(Callback->Open_CheckBreak()); } - *inStream = NULL; - FString fullPath = _folderPrefix + us2fs(name); + + UString name2 = name; + + + #ifndef _SFX + + #ifdef _WIN32 + name2.Replace(L'/', WCHAR_PATH_SEPARATOR); + #endif + + // if (!allowAbsVolPaths) + if (!IsSafePath(name2)) + return S_FALSE; + + #endif + + + FString fullPath; + if (!NFile::NName::GetFullPath(_folderPrefix, us2fs(name2), fullPath)) + return S_FALSE; if (!_fileInfo.Find(fullPath)) return S_FALSE; if (_fileInfo.IsDir()) @@ -101,13 +115,20 @@ STDMETHODIMP COpenCallbackImp::GetStream(const wchar_t *name, IInStream **inStre CInFileStreamVol *inFile = new CInFileStreamVol; CMyComPtr<IInStream> inStreamTemp = inFile; if (!inFile->Open(fullPath)) - return ::GetLastError(); - *inStream = inStreamTemp.Detach(); - inFile->Name = name; + { + DWORD lastError = ::GetLastError(); + if (lastError == 0) + return E_FAIL; + return HRESULT_FROM_WIN32(lastError); + } + + FileSizes.Add(_fileInfo.Size); + FileNames.Add(name2); + inFile->FileNameIndex = FileNames_WasUsed.Add(true); inFile->OpenCallbackImp = this; inFile->OpenCallbackRef = this; - FileNames.Add(name); - TotalSize += _fileInfo.Size; + // TotalSize += _fileInfo.Size; + *inStream = inStreamTemp.Detach(); return S_OK; COM_TRY_END } @@ -125,6 +146,7 @@ STDMETHODIMP COpenCallbackImp::CryptoGetTextPassword(BSTR *password) } if (!Callback) return E_NOTIMPL; + PasswordWasAsked = true; return Callback->Open_CryptoGetTextPassword(password); COM_TRY_END } diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveOpenCallback.h b/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveOpenCallback.h index afada4a6352..e9575f56123 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveOpenCallback.h +++ b/3rdparty/lzma/CPP/7zip/UI/Common/ArchiveOpenCallback.h @@ -3,10 +3,9 @@ #ifndef __ARCHIVE_OPEN_CALLBACK_H #define __ARCHIVE_OPEN_CALLBACK_H -#include "Common/MyCom.h" -#include "Common/MyString.h" +#include "../../../Common/MyCom.h" -#include "Windows/FileFind.h" +#include "../../../Windows/FileFind.h" #ifndef _NO_CRYPTO #include "../../IPassword.h" @@ -21,9 +20,9 @@ #define INTERFACE_IOpenCallbackUI_Crypto(x) \ virtual HRESULT Open_CryptoGetTextPassword(BSTR *password) x; \ - virtual HRESULT Open_GetPasswordIfAny(UString &password) x; \ - virtual bool Open_WasPasswordAsked() x; \ - virtual void Open_ClearPasswordWasAskedFlag() x; \ + /* virtual HRESULT Open_GetPasswordIfAny(bool &passwordIsDefined, UString &password) x; */ \ + /* virtual bool Open_WasPasswordAsked() x; */ \ + /* virtual void Open_Clear_PasswordWasAsked_Flag() x; */ \ #endif @@ -31,6 +30,7 @@ virtual HRESULT Open_CheckBreak() x; \ virtual HRESULT Open_SetTotal(const UInt64 *files, const UInt64 *bytes) x; \ virtual HRESULT Open_SetCompleted(const UInt64 *files, const UInt64 *bytes) x; \ + virtual HRESULT Open_Finished() x; \ INTERFACE_IOpenCallbackUI_Crypto(x) struct IOpenCallbackUI @@ -48,18 +48,13 @@ class COpenCallbackImp: public CMyUnknownImp { public: + MY_QUERYINTERFACE_BEGIN2(IArchiveOpenVolumeCallback) + MY_QUERYINTERFACE_ENTRY(IArchiveOpenSetSubArchiveName) #ifndef _NO_CRYPTO - MY_UNKNOWN_IMP3( - IArchiveOpenVolumeCallback, - ICryptoGetTextPassword, - IArchiveOpenSetSubArchiveName - ) - #else - MY_UNKNOWN_IMP2( - IArchiveOpenVolumeCallback, - IArchiveOpenSetSubArchiveName - ) + MY_QUERYINTERFACE_ENTRY(ICryptoGetTextPassword) #endif + MY_QUERYINTERFACE_END + MY_ADDREF_RELEASE INTERFACE_IArchiveOpenCallback(;) INTERFACE_IArchiveOpenVolumeCallback(;) @@ -72,8 +67,8 @@ public: { _subArchiveMode = true; _subArchiveName = name; - TotalSize = 0; - return S_OK; + // TotalSize = 0; + return S_OK; } private: @@ -81,23 +76,37 @@ private: NWindows::NFile::NFind::CFileInfo _fileInfo; bool _subArchiveMode; UString _subArchiveName; + public: UStringVector FileNames; + CBoolVector FileNames_WasUsed; + CRecordVector<UInt64> FileSizes; + + bool PasswordWasAsked; + IOpenCallbackUI *Callback; CMyComPtr<IArchiveOpenCallback> ReOpenCallback; - UInt64 TotalSize; + // UInt64 TotalSize; - COpenCallbackImp(): Callback(NULL) {} + COpenCallbackImp(): Callback(NULL), _subArchiveMode(false) {} + void Init(const FString &folderPrefix, const FString &fileName) { _folderPrefix = folderPrefix; if (!_fileInfo.Find(_folderPrefix + fileName)) - throw 1; + throw 20121118; FileNames.Clear(); + FileNames_WasUsed.Clear(); + FileSizes.Clear(); _subArchiveMode = false; - TotalSize = 0; + // TotalSize = 0; + PasswordWasAsked = false; + } + + bool SetSecondFileInfo(CFSTR newName) + { + return _fileInfo.Find(newName) && !_fileInfo.IsDir(); } - int FindName(const UString &name); }; #endif diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/Bench.cpp b/3rdparty/lzma/CPP/7zip/UI/Common/Bench.cpp index 5adfcc20552..fb69b544414 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/Bench.cpp +++ b/3rdparty/lzma/CPP/7zip/UI/Common/Bench.cpp @@ -2,6 +2,8 @@ #include "StdAfx.h" +#include <stdio.h> + #ifndef _WIN32 #define USE_POSIX_TIME #define USE_POSIX_TIME2 @@ -28,39 +30,65 @@ #include "../../../../C/7zCrc.h" #include "../../../../C/Alloc.h" +#include "../../../../C/CpuArch.h" -#if !defined(_7ZIP_ST) || defined(_WIN32) #include "../../../Windows/System.h" -#endif #ifndef _7ZIP_ST #include "../../../Windows/Synchronization.h" #include "../../../Windows/Thread.h" #endif +#if defined(_WIN32) || defined(UNIX_USE_WIN_FILE) +#define USE_WIN_FILE +#endif + +#ifdef USE_WIN_FILE +#include "../../../Windows/FileIO.h" +#endif + + #include "../../../Common/IntToString.h" #include "../../../Common/StringConvert.h" #include "../../../Common/StringToInt.h" #include "../../Common/MethodProps.h" +#include "../../Common/StreamUtils.h" #include "Bench.h" using namespace NWindows; -static const UInt64 kUncompressMinBlockSize = -#ifdef UNDER_CE -(UInt64)1 << 30; -#else -(UInt64)1 << 33; -#endif +static const UInt32 k_LZMA = 0x030101; -static const UInt32 kCrcBlockSize = -#ifdef UNDER_CE -1 << 25; -#else -1 << 30; -#endif +static const UInt64 kComplexInCommands = (UInt64)1 << + #ifdef UNDER_CE + 31; + #else + 34; + #endif + +static const UInt32 kComplexInSeconds = 4; + +static void SetComplexCommands(UInt32 complexInSeconds, + bool isSpecifiedFreq, UInt64 cpuFreq, UInt64 &complexInCommands) +{ + complexInCommands = kComplexInCommands; + const UInt64 kMinFreq = (UInt64)1000000 * 4; + const UInt64 kMaxFreq = (UInt64)1000000 * 20000; + if (cpuFreq < kMinFreq && !isSpecifiedFreq) + cpuFreq = kMinFreq; + if (cpuFreq < kMaxFreq || isSpecifiedFreq) + { + if (complexInSeconds != 0) + complexInCommands = complexInSeconds * cpuFreq; + else + complexInCommands = cpuFreq >> 2; + } +} + +static const unsigned kNumHashDictBits = 17; +static const UInt32 kFilterUnpackSize = (48 << 10); static const unsigned kOldLzmaDictBits = 30; @@ -83,79 +111,154 @@ public: } }; -class CBenchBuffer + +static const unsigned kBufferAlignment = 1 << 4; + +struct CBenchBuffer { -public: size_t BufferSize; + + #ifdef _WIN32 + Byte *Buffer; - CBenchBuffer(): Buffer(0) {} - virtual ~CBenchBuffer() { Free(); } - void Free() + + CBenchBuffer(): BufferSize(0), Buffer(NULL) {} + ~CBenchBuffer() { ::MidFree(Buffer); } + + void AllocAlignedMask(size_t size, size_t) { ::MidFree(Buffer); - Buffer = 0; + BufferSize = 0; + Buffer = (Byte *)::MidAlloc(size); + if (Buffer) + BufferSize = size; } - bool Alloc(size_t bufferSize) + + #else + + Byte *Buffer; + Byte *_bufBase; + + CBenchBuffer(): BufferSize(0), Buffer(NULL), _bufBase(NULL){} + ~CBenchBuffer() { ::MidFree(_bufBase); } + + void AllocAlignedMask(size_t size, size_t alignMask) { - if (Buffer != 0 && BufferSize == bufferSize) + ::MidFree(_bufBase); + Buffer = NULL; + BufferSize = 0; + _bufBase = (Byte *)::MidAlloc(size + alignMask); + + if (_bufBase) + { + // Buffer = (Byte *)(((uintptr_t)_bufBase + alignMask) & ~(uintptr_t)alignMask); + Buffer = (Byte *)(((ptrdiff_t)_bufBase + alignMask) & ~(ptrdiff_t)alignMask); + BufferSize = size; + } + } + + #endif + + bool Alloc(size_t size) + { + if (Buffer && BufferSize == size) return true; - Free(); - Buffer = (Byte *)::MidAlloc(bufferSize); - BufferSize = bufferSize; - return (Buffer != 0); + AllocAlignedMask(size, kBufferAlignment - 1); + return (Buffer != NULL || size == 0); } }; + class CBenchRandomGenerator: public CBenchBuffer { - CBaseRandomGenerator *RG; -public: - void Set(CBaseRandomGenerator *rg) { RG = rg; } - UInt32 GetVal(UInt32 &res, unsigned numBits) + static UInt32 GetVal(UInt32 &res, unsigned numBits) { UInt32 val = res & (((UInt32)1 << numBits) - 1); res >>= numBits; return val; } - UInt32 GetLen(UInt32 &res) + + static UInt32 GetLen(UInt32 &r) { - UInt32 len = GetVal(res, 2); - return GetVal(res, 1 + len); + UInt32 len = GetVal(r, 2); + return GetVal(r, 1 + len); } - void Generate(unsigned dictBits) + +public: + + void GenerateSimpleRandom(CBaseRandomGenerator *_RG_) + { + CBaseRandomGenerator rg = *_RG_; + const size_t bufSize = BufferSize; + Byte *buf = Buffer; + for (size_t i = 0; i < bufSize; i++) + buf[i] = (Byte)rg.GetRnd(); + *_RG_ = rg; + } + + void GenerateLz(unsigned dictBits, CBaseRandomGenerator *_RG_) { + CBaseRandomGenerator rg = *_RG_; UInt32 pos = 0; UInt32 rep0 = 1; - while (pos < BufferSize) + const size_t bufSize = BufferSize; + Byte *buf = Buffer; + unsigned posBits = 1; + + while (pos < bufSize) { - UInt32 res = RG->GetRnd(); - res >>= 1; - if (GetVal(res, 1) == 0 || pos < 1024) - Buffer[pos++] = (Byte)(res & 0xFF); + UInt32 r = rg.GetRnd(); + if (GetVal(r, 1) == 0 || pos < 1024) + buf[pos++] = (Byte)(r & 0xFF); else { UInt32 len; - len = 1 + GetLen(res); - if (GetVal(res, 3) != 0) + len = 1 + GetLen(r); + + if (GetVal(r, 3) != 0) { - len += GetLen(res); - do + len += GetLen(r); + + while (((UInt32)1 << posBits) < pos) + posBits++; + + unsigned numBitsMax = dictBits; + if (numBitsMax > posBits) + numBitsMax = posBits; + + const unsigned kAddBits = 6; + unsigned numLogBits = 5; + if (numBitsMax <= (1 << 4) - 1 + kAddBits) + numLogBits = 4; + + for (;;) { - UInt32 ppp = GetVal(res, 5) + 6; - res = RG->GetRnd(); - if (ppp > dictBits) + UInt32 ppp = GetVal(r, numLogBits) + kAddBits; + r = rg.GetRnd(); + if (ppp > numBitsMax) continue; - rep0 = /* (1 << ppp) +*/ GetVal(res, ppp); - res = RG->GetRnd(); + rep0 = GetVal(r, ppp); + if (rep0 < pos) + break; + r = rg.GetRnd(); } - while (rep0 >= pos); rep0++; } - for (UInt32 i = 0; i < len && pos < BufferSize; i++, pos++) - Buffer[pos] = Buffer[pos - rep0]; + { + UInt32 rem = (UInt32)bufSize - pos; + if (len > rem) + len = rem; + } + Byte *dest = buf + pos; + const Byte *src = dest - rep0; + pos += len; + for (UInt32 i = 0; i < len; i++) + *dest++ = *src++; } } + + *_RG_ = rg; } }; @@ -189,7 +292,7 @@ STDMETHODIMP CBenchmarkInStream::Read(void *data, UInt32 size, UInt32 *processed for (UInt32 i = 0; i < size; i++) ((Byte *)data)[i] = Data[Pos + i]; Pos += size; - if(processedSize != NULL) + if (processedSize) *processedSize = size; return S_OK; } @@ -201,13 +304,23 @@ class CBenchmarkOutStream: { // bool _overflow; public: - UInt32 Pos; + size_t Pos; + bool RealCopy; + bool CalcCrc; + UInt32 Crc; + // CBenchmarkOutStream(): _overflow(false) {} - void Init() + void Init(bool realCopy, bool calcCrc) { + Crc = CRC_INIT_VAL; + RealCopy = realCopy; + CalcCrc = calcCrc; // _overflow = false; Pos = 0; } + + // void Print() { printf("\n%8d %8d\n", (unsigned)BufferSize, (unsigned)Pos); } + MY_UNKNOWN_IMP STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); }; @@ -217,9 +330,15 @@ STDMETHODIMP CBenchmarkOutStream::Write(const void *data, UInt32 size, UInt32 *p size_t curSize = BufferSize - Pos; if (curSize > size) curSize = size; - memcpy(Buffer + Pos, data, curSize); - Pos += (UInt32)curSize; - if(processedSize != NULL) + if (curSize != 0) + { + if (RealCopy) + memcpy(Buffer + Pos, data, curSize); + if (CalcCrc) + Crc = CrcUpdate(Crc, data, curSize); + Pos += curSize; + } + if (processedSize) *processedSize = (UInt32)curSize; if (curSize != size) { @@ -234,16 +353,20 @@ class CCrcOutStream: public CMyUnknownImp { public: + bool CalcCrc; UInt32 Crc; MY_UNKNOWN_IMP + + CCrcOutStream(): CalcCrc(true) {}; void Init() { Crc = CRC_INIT_VAL; } STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); }; STDMETHODIMP CCrcOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { - Crc = CrcUpdate(Crc, data, size); - if (processedSize != NULL) + if (CalcCrc) + Crc = CrcUpdate(Crc, data, size); + if (processedSize) *processedSize = size; return S_OK; } @@ -369,25 +492,16 @@ public: } }; -class CBenchProgressInfo: - public ICompressProgressInfo, - public CMyUnknownImp +struct CBenchInfoCalc { -public: - CBenchProgressStatus *Status; CBenchInfo BenchInfo; CUserTime UserTime; - HRESULT Res; - IBenchCallback *Callback; - CBenchProgressInfo(): Callback(0) {} void SetStartTime(); void SetFinishTime(CBenchInfo &dest); - MY_UNKNOWN_IMP - STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); }; -void CBenchProgressInfo::SetStartTime() +void CBenchInfoCalc::SetStartTime() { BenchInfo.GlobalFreq = GetFreq(); BenchInfo.UserFreq = GetUserFreq(); @@ -396,13 +510,28 @@ void CBenchProgressInfo::SetStartTime() UserTime.Init(); } -void CBenchProgressInfo::SetFinishTime(CBenchInfo &dest) +void CBenchInfoCalc::SetFinishTime(CBenchInfo &dest) { dest = BenchInfo; dest.GlobalTime = ::GetTimeCount() - BenchInfo.GlobalTime; dest.UserTime = UserTime.GetUserTime(); } +class CBenchProgressInfo: + public ICompressProgressInfo, + public CMyUnknownImp, + public CBenchInfoCalc +{ +public: + CBenchProgressStatus *Status; + HRESULT Res; + IBenchCallback *Callback; + + CBenchProgressInfo(): Callback(0) {} + MY_UNKNOWN_IMP + STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); +}; + STDMETHODIMP CBenchProgressInfo::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) { HRESULT res = Status->GetResult(); @@ -414,8 +543,8 @@ STDMETHODIMP CBenchProgressInfo::SetRatioInfo(const UInt64 *inSize, const UInt64 SetFinishTime(info); if (Status->EncodeMode) { - info.UnpackSize = *inSize; - info.PackSize = *outSize; + info.UnpackSize = BenchInfo.UnpackSize + *inSize; + info.PackSize = BenchInfo.PackSize + *outSize; res = Callback->SetEncodeResult(info, false); } else @@ -429,11 +558,11 @@ STDMETHODIMP CBenchProgressInfo::SetRatioInfo(const UInt64 *inSize, const UInt64 return res; } -static const int kSubBits = 8; +static const unsigned kSubBits = 8; static UInt32 GetLogSize(UInt32 size) { - for (int i = kSubBits; i < 32; i++) + for (unsigned i = kSubBits; i < 32; i++) for (UInt32 j = 0; j < (1 << kSubBits); j++) if (size <= (((UInt32)1) << i) + (j << (i - kSubBits))) return (i << kSubBits) + j; @@ -488,6 +617,11 @@ static UInt64 MyMultDiv64(UInt64 value, UInt64 elapsedTime, UInt64 freq) return value * freq / elTime; } +UInt64 CBenchInfo::GetSpeed(UInt64 numCommands) const +{ + return MyMultDiv64(numCommands, GlobalTime, GlobalFreq); +} + struct CBenchProps { bool LzmaRatingMode; @@ -499,19 +633,25 @@ struct CBenchProps CBenchProps(): LzmaRatingMode(false) {} void SetLzmaCompexity(); + UInt64 GeComprCommands(UInt64 unpackSize) + { + return unpackSize * EncComplex; + } + UInt64 GeDecomprCommands(UInt64 packSize, UInt64 unpackSize) { return (packSize * DecComplexCompr + unpackSize * DecComplexUnc); } UInt64 GetCompressRating(UInt32 dictSize, UInt64 elapsedTime, UInt64 freq, UInt64 size); - UInt64 GetDecompressRating(UInt64 elapsedTime, UInt64 freq, UInt64 outSize, UInt64 inSize, UInt32 numIterations); + UInt64 GetDecompressRating(UInt64 elapsedTime, UInt64 freq, UInt64 outSize, UInt64 inSize, UInt64 numIterations); }; void CBenchProps::SetLzmaCompexity() { + EncComplex = 1200; DecComplexUnc = 4; - DecComplexCompr = 200; + DecComplexCompr = 190; LzmaRatingMode = true; } @@ -529,7 +669,7 @@ UInt64 CBenchProps::GetCompressRating(UInt32 dictSize, UInt64 elapsedTime, UInt6 return MyMultDiv64(numCommands, elapsedTime, freq); } -UInt64 CBenchProps::GetDecompressRating(UInt64 elapsedTime, UInt64 freq, UInt64 outSize, UInt64 inSize, UInt32 numIterations) +UInt64 CBenchProps::GetDecompressRating(UInt64 elapsedTime, UInt64 freq, UInt64 outSize, UInt64 inSize, UInt64 numIterations) { UInt64 numCommands = (inSize * DecComplexCompr + outSize * DecComplexUnc) * numIterations; return MyMultDiv64(numCommands, elapsedTime, freq); @@ -542,7 +682,7 @@ UInt64 GetCompressRating(UInt32 dictSize, UInt64 elapsedTime, UInt64 freq, UInt6 return props.GetCompressRating(dictSize, elapsedTime, freq, size); } -UInt64 GetDecompressRating(UInt64 elapsedTime, UInt64 freq, UInt64 outSize, UInt64 inSize, UInt32 numIterations) +UInt64 GetDecompressRating(UInt64 elapsedTime, UInt64 freq, UInt64 outSize, UInt64 inSize, UInt64 numIterations) { CBenchProps props; props.SetLzmaCompexity(); @@ -557,67 +697,103 @@ struct CEncoderInfo NWindows::CThread thread[2]; UInt32 NumDecoderSubThreads; #endif - CMyComPtr<ICompressCoder> encoder; + CMyComPtr<ICompressCoder> _encoder; + CMyComPtr<ICompressFilter> _encoderFilter; CBenchProgressInfo *progressInfoSpec[2]; CMyComPtr<ICompressProgressInfo> progressInfo[2]; - UInt32 NumIterations; + UInt64 NumIterations; + #ifdef USE_ALLOCA size_t AllocaSize; #endif + Byte _key[32]; + Byte _iv[16]; + Byte _psw[16]; + bool CheckCrc_Enc; + bool CheckCrc_Dec; + struct CDecoderInfo { CEncoderInfo *Encoder; UInt32 DecoderIndex; + bool CallbackMode; + #ifdef USE_ALLOCA size_t AllocaSize; #endif - bool CallbackMode; }; CDecoderInfo decodersInfo[2]; - CMyComPtr<ICompressCoder> decoders[2]; + CMyComPtr<ICompressCoder> _decoders[2]; + CMyComPtr<ICompressFilter> _decoderFilter; + HRESULT Results[2]; CBenchmarkOutStream *outStreamSpec; CMyComPtr<ISequentialOutStream> outStream; IBenchCallback *callback; IBenchPrintCallback *printCallback; UInt32 crc; - UInt32 kBufferSize; - UInt32 compressedSize; + size_t kBufferSize; + size_t compressedSize; + const Byte *uncompressedDataPtr; + + const Byte *fileData; CBenchRandomGenerator rg; + + CBenchBuffer rgCopy; // it must be 16-byte aligned !!! CBenchmarkOutStream *propStreamSpec; CMyComPtr<ISequentialOutStream> propStream; + + // for decode + COneMethodInfo _method; + size_t _uncompressedDataSize; + HRESULT Init( const COneMethodInfo &method, - UInt32 uncompressedDataSize, unsigned generateDictBits, CBaseRandomGenerator *rg); HRESULT Encode(); HRESULT Decode(UInt32 decoderIndex); - CEncoderInfo(): outStreamSpec(0), callback(0), printCallback(0), propStreamSpec(0) {} + CEncoderInfo(): + fileData(NULL), + CheckCrc_Enc(true), + CheckCrc_Dec(true), + outStreamSpec(0), callback(0), printCallback(0), propStreamSpec(0) {} #ifndef _7ZIP_ST + static THREAD_FUNC_DECL EncodeThreadFunction(void *param) { + HRESULT res; CEncoderInfo *encoder = (CEncoderInfo *)param; - #ifdef USE_ALLOCA - alloca(encoder->AllocaSize); - #endif - HRESULT res = encoder->Encode(); - encoder->Results[0] = res; + try + { + #ifdef USE_ALLOCA + alloca(encoder->AllocaSize); + #endif + + res = encoder->Encode(); + encoder->Results[0] = res; + } + catch(...) + { + res = E_FAIL; + } if (res != S_OK) encoder->progressInfoSpec[0]->Status->SetResult(res); - return 0; } + static THREAD_FUNC_DECL DecodeThreadFunction(void *param) { CDecoderInfo *decoder = (CDecoderInfo *)param; + #ifdef USE_ALLOCA alloca(decoder->AllocaSize); #endif + CEncoderInfo *encoder = decoder->Encoder; encoder->Results[decoder->DecoderIndex] = encoder->Decode(decoder->DecoderIndex); return 0; @@ -628,7 +804,7 @@ struct CEncoderInfo return thread[0].Create(EncodeThreadFunction, this); } - HRESULT CreateDecoderThread(int index, bool callbackMode + HRESULT CreateDecoderThread(unsigned index, bool callbackMode #ifdef USE_ALLOCA , size_t allocaSize #endif @@ -637,30 +813,57 @@ struct CEncoderInfo CDecoderInfo &decoder = decodersInfo[index]; decoder.DecoderIndex = index; decoder.Encoder = this; + #ifdef USE_ALLOCA decoder.AllocaSize = allocaSize; #endif + decoder.CallbackMode = callbackMode; return thread[index].Create(DecodeThreadFunction, &decoder); } + #endif }; -static const UInt32 k_LZMA = 0x030101; HRESULT CEncoderInfo::Init( const COneMethodInfo &method, - UInt32 uncompressedDataSize, unsigned generateDictBits, CBaseRandomGenerator *rgLoc) { - rg.Set(rgLoc); - kBufferSize = uncompressedDataSize; - UInt32 kCompressedBufferSize = (kBufferSize - kBufferSize / 4) + kCompressedAdditionalSize; - if (!rg.Alloc(kBufferSize)) - return E_OUTOFMEMORY; - rg.Generate(generateDictBits); - crc = CrcCalc(rg.Buffer, rg.BufferSize); + // we need extra space, if input data is already compressed + const size_t kCompressedBufferSize = + kCompressedAdditionalSize + + kBufferSize + kBufferSize / 16; + // kBufferSize / 2; + + if (kCompressedBufferSize < kBufferSize) + return E_FAIL; + + uncompressedDataPtr = fileData; + + if (!fileData) + { + if (!rg.Alloc(kBufferSize)) + return E_OUTOFMEMORY; + + // DWORD ttt = GetTickCount(); + if (generateDictBits == 0) + rg.GenerateSimpleRandom(rgLoc); + else + rg.GenerateLz(generateDictBits, rgLoc); + // printf("\n%d\n ", GetTickCount() - ttt); + + crc = CrcCalc(rg.Buffer, rg.BufferSize); + uncompressedDataPtr = rg.Buffer; + } + + if (_encoderFilter) + { + if (!rgCopy.Alloc(kBufferSize)) + return E_OUTOFMEMORY; + } + outStreamSpec = new CBenchmarkOutStream; if (!outStreamSpec->Alloc(kCompressedBufferSize)) @@ -676,54 +879,174 @@ HRESULT CEncoderInfo::Init( } if (!propStreamSpec->Alloc(kMaxLzmaPropSize)) return E_OUTOFMEMORY; - propStreamSpec->Init(); + propStreamSpec->Init(true, false); + CMyComPtr<IUnknown> coder; + if (_encoderFilter) + coder = _encoderFilter; + else + coder = _encoder; { CMyComPtr<ICompressSetCoderProperties> scp; - encoder.QueryInterface(IID_ICompressSetCoderProperties, &scp); + coder.QueryInterface(IID_ICompressSetCoderProperties, &scp); if (scp) { - UInt64 reduceSize = uncompressedDataSize; + UInt64 reduceSize = kBufferSize; RINOK(method.SetCoderProps(scp, &reduceSize)); } else { if (method.AreThereNonOptionalProps()) - return E_FAIL; + return E_INVALIDARG; } CMyComPtr<ICompressWriteCoderProperties> writeCoderProps; - encoder.QueryInterface(IID_ICompressWriteCoderProperties, &writeCoderProps); + coder.QueryInterface(IID_ICompressWriteCoderProperties, &writeCoderProps); if (writeCoderProps) { RINOK(writeCoderProps->WriteCoderProperties(propStream)); } + + { + CMyComPtr<ICryptoSetPassword> sp; + coder.QueryInterface(IID_ICryptoSetPassword, &sp); + if (sp) + { + RINOK(sp->CryptoSetPassword(_psw, sizeof(_psw))); + + // we must call encoding one time to calculate password key for key cache. + // it must be after WriteCoderProperties! + CBenchmarkInStream *inStreamSpec = new CBenchmarkInStream; + CMyComPtr<ISequentialInStream> inStream = inStreamSpec; + Byte temp[16]; + memset(temp, 0, sizeof(temp)); + inStreamSpec->Init(temp, sizeof(temp)); + + CCrcOutStream *outStreamSpec = new CCrcOutStream; + CMyComPtr<ISequentialOutStream> outStream = outStreamSpec; + outStreamSpec->Init(); + + if (_encoderFilter) + { + _encoderFilter->Init(); + _encoderFilter->Filter(temp, sizeof(temp)); + } + else + { + RINOK(_encoder->Code(inStream, outStream, 0, 0, NULL)); + } + } + } } + return S_OK; } + +static void My_FilterBench(ICompressFilter *filter, Byte *data, size_t size) +{ + while (size != 0) + { + UInt32 cur = (UInt32)1 << 31; + if (cur > size) + cur = (UInt32)size; + UInt32 processed = filter->Filter(data, cur); + data += processed; + // if (processed > size) (in AES filter), we must fill last block with zeros. + // but it is not important for benchmark. So we just copy that data without filtering. + if (processed > size || processed == 0) + break; + size -= processed; + } +} + + HRESULT CEncoderInfo::Encode() { + CBenchInfo &bi = progressInfoSpec[0]->BenchInfo; + bi.UnpackSize = 0; + bi.PackSize = 0; + CMyComPtr<ICryptoProperties> cp; + CMyComPtr<IUnknown> coder; + if (_encoderFilter) + coder = _encoderFilter; + else + coder = _encoder; + coder.QueryInterface(IID_ICryptoProperties, &cp); CBenchmarkInStream *inStreamSpec = new CBenchmarkInStream; CMyComPtr<ISequentialInStream> inStream = inStreamSpec; - inStreamSpec->Init(rg.Buffer, rg.BufferSize); - outStreamSpec->Init(); + UInt64 prev = 0; - RINOK(encoder->Code(inStream, outStream, 0, 0, progressInfo[0])); - compressedSize = outStreamSpec->Pos; - encoder.Release(); + UInt32 crcPrev = 0; + + if (cp) + { + RINOK(cp->SetKey(_key, sizeof(_key))); + RINOK(cp->SetInitVector(_iv, sizeof(_iv))); + } + + for (UInt64 i = 0; i < NumIterations; i++) + { + if (printCallback && bi.UnpackSize - prev > (1 << 20)) + { + RINOK(printCallback->CheckBreak()); + prev = bi.UnpackSize; + } + + bool isLast = (i == NumIterations - 1); + bool calcCrc = ((isLast || (i & 0x7F) == 0 || CheckCrc_Enc) && NumIterations != 1); + outStreamSpec->Init(isLast, calcCrc); + + if (_encoderFilter) + { + memcpy(rgCopy.Buffer, uncompressedDataPtr, kBufferSize); + _encoderFilter->Init(); + My_FilterBench(_encoderFilter, rgCopy.Buffer, kBufferSize); + RINOK(WriteStream(outStream, rgCopy.Buffer, kBufferSize)); + } + else + { + inStreamSpec->Init(uncompressedDataPtr, kBufferSize); + RINOK(_encoder->Code(inStream, outStream, NULL, NULL, progressInfo[0])); + } + + // outStreamSpec->Print(); + + UInt32 crcNew = CRC_GET_DIGEST(outStreamSpec->Crc); + if (i == 0) + crcPrev = crcNew; + else if (calcCrc && crcPrev != crcNew) + return E_FAIL; + + compressedSize = outStreamSpec->Pos; + bi.UnpackSize += kBufferSize; + bi.PackSize += compressedSize; + } + + _encoder.Release(); + _encoderFilter.Release(); return S_OK; } + HRESULT CEncoderInfo::Decode(UInt32 decoderIndex) { CBenchmarkInStream *inStreamSpec = new CBenchmarkInStream; CMyComPtr<ISequentialInStream> inStream = inStreamSpec; - CMyComPtr<ICompressCoder> &decoder = decoders[decoderIndex]; + CMyComPtr<ICompressCoder> &decoder = _decoders[decoderIndex]; + CMyComPtr<IUnknown> coder; + if (_decoderFilter) + { + if (decoderIndex != 0) + return E_FAIL; + coder = _decoderFilter; + } + else + coder = decoder; CMyComPtr<ICompressSetDecoderProperties2> setDecProps; - decoder.QueryInterface(IID_ICompressSetDecoderProperties2, &setDecProps); + coder.QueryInterface(IID_ICompressSetDecoderProperties2, &setDecProps); if (!setDecProps && propStreamSpec->Pos != 0) return E_FAIL; @@ -737,7 +1060,7 @@ HRESULT CEncoderInfo::Decode(UInt32 decoderIndex) #ifndef _7ZIP_ST { CMyComPtr<ICompressSetCoderMt> setCoderMt; - decoder.QueryInterface(IID_ICompressSetCoderMt, &setCoderMt); + coder.QueryInterface(IID_ICompressSetCoderMt, &setCoderMt); if (setCoderMt) { RINOK(setCoderMt->SetNumberOfThreads(NumDecoderSubThreads)); @@ -745,30 +1068,79 @@ HRESULT CEncoderInfo::Decode(UInt32 decoderIndex) } #endif - for (UInt32 j = 0; j < NumIterations; j++) + CMyComPtr<ICompressSetCoderProperties> scp; + coder.QueryInterface(IID_ICompressSetCoderProperties, &scp); + if (scp) { - if (printCallback) + UInt64 reduceSize = _uncompressedDataSize; + RINOK(_method.SetCoderProps(scp, &reduceSize)); + } + + CMyComPtr<ICryptoProperties> cp; + coder.QueryInterface(IID_ICryptoProperties, &cp); + + if (setDecProps) + { + RINOK(setDecProps->SetDecoderProperties2(propStreamSpec->Buffer, (UInt32)propStreamSpec->Pos)); + } + + { + CMyComPtr<ICryptoSetPassword> sp; + coder.QueryInterface(IID_ICryptoSetPassword, &sp); + if (sp) + { + RINOK(sp->CryptoSetPassword(_psw, sizeof(_psw))); + } + } + + UInt64 prev = 0; + + if (cp) + { + RINOK(cp->SetKey(_key, sizeof(_key))); + RINOK(cp->SetInitVector(_iv, sizeof(_iv))); + } + + for (UInt64 i = 0; i < NumIterations; i++) + { + if (printCallback && pi->BenchInfo.UnpackSize - prev > (1 << 20)) { RINOK(printCallback->CheckBreak()); + prev = pi->BenchInfo.UnpackSize; } + inStreamSpec->Init(outStreamSpec->Buffer, compressedSize); crcOutStreamSpec->Init(); - if (setDecProps) + UInt64 outSize = kBufferSize; + crcOutStreamSpec->CalcCrc = ((i & 0x7F) == 0 || CheckCrc_Dec); + + if (_decoderFilter) { - RINOK(setDecProps->SetDecoderProperties2(propStreamSpec->Buffer, propStreamSpec->Pos)); + if (compressedSize > rgCopy.BufferSize) + return E_FAIL; + memcpy(rgCopy.Buffer, outStreamSpec->Buffer, compressedSize); + _decoderFilter->Init(); + My_FilterBench(_decoderFilter, rgCopy.Buffer, compressedSize); + RINOK(WriteStream(crcOutStream, rgCopy.Buffer, compressedSize)); } - UInt64 outSize = kBufferSize; - RINOK(decoder->Code(inStream, crcOutStream, 0, &outSize, progressInfo[decoderIndex])); - if (CRC_GET_DIGEST(crcOutStreamSpec->Crc) != crc) + else + { + RINOK(decoder->Code(inStream, crcOutStream, 0, &outSize, progressInfo[decoderIndex])); + } + + if (crcOutStreamSpec->CalcCrc && CRC_GET_DIGEST(crcOutStreamSpec->Crc) != crc) return S_FALSE; pi->BenchInfo.UnpackSize += kBufferSize; pi->BenchInfo.PackSize += compressedSize; } + decoder.Release(); + _decoderFilter.Release(); return S_OK; } + static const UInt32 kNumThreadsMax = (1 << 12); struct CBenchEncoders @@ -778,25 +1150,46 @@ struct CBenchEncoders ~CBenchEncoders() { delete []encoders; } }; + +static UInt64 GetNumIterations(UInt64 numCommands, UInt64 complexInCommands) +{ + if (numCommands < (1 << 4)) + numCommands = (1 << 4); + UInt64 res = complexInCommands / numCommands; + return (res == 0 ? 1 : res); +} + + static HRESULT MethodBench( DECL_EXTERNAL_CODECS_LOC_VARS - bool oldLzmaBenchMode, - UInt32 numThreads, + UInt64 complexInCommands, + bool + #ifndef _7ZIP_ST + oldLzmaBenchMode + #endif + , + UInt32 + #ifndef _7ZIP_ST + numThreads + #endif + , const COneMethodInfo &method2, - UInt32 uncompressedDataSize, + size_t uncompressedDataSize, + const Byte *fileData, unsigned generateDictBits, + IBenchPrintCallback *printCallback, IBenchCallback *callback, CBenchProps *benchProps) { COneMethodInfo method = method2; UInt64 methodId; - UInt32 numInStreams, numOutStreams; + UInt32 numStreams; if (!FindMethod( EXTERNAL_CODECS_LOC_VARS - method.MethodName, methodId, numInStreams, numOutStreams)) + method.MethodName, methodId, numStreams)) return E_NOTIMPL; - if (numInStreams != 1 || numOutStreams != 1) + if (numStreams != 1) return E_INVALIDARG; UInt32 numEncoderThreads = 1; @@ -810,7 +1203,7 @@ static HRESULT MethodBench( bool fixedNumber; UInt32 numLzmaThreads = method.Get_Lzma_NumThreads(fixedNumber); if (!fixedNumber && numThreads == 1) - method.AddNumThreadsProp(1); + method.AddProp_NumThreads(1); if (numThreads > 1 && numLzmaThreads > 1) { numEncoderThreads = numThreads / 2; @@ -819,35 +1212,60 @@ static HRESULT MethodBench( } #endif - if (numThreads < 1 || numEncoderThreads > kNumThreadsMax) - return E_INVALIDARG; - CBenchEncoders encodersSpec(numEncoderThreads); CEncoderInfo *encoders = encodersSpec.encoders; UInt32 i; + for (i = 0; i < numEncoderThreads; i++) { CEncoderInfo &encoder = encoders[i]; encoder.callback = (i == 0) ? callback : 0; encoder.printCallback = printCallback; - RINOK(CreateCoder(EXTERNAL_CODECS_LOC_VARS methodId, encoder.encoder, true)); - if (!encoder.encoder) - return E_NOTIMPL; + { + CCreatedCoder cod; + RINOK(CreateCoder(EXTERNAL_CODECS_LOC_VARS methodId, true, encoder._encoderFilter, cod)); + encoder._encoder = cod.Coder; + if (!encoder._encoder && !encoder._encoderFilter) + return E_NOTIMPL; + } + + encoder.CheckCrc_Enc = (benchProps->EncComplex) > 30 ; + encoder.CheckCrc_Dec = (benchProps->DecComplexCompr + benchProps->DecComplexUnc) > 30 ; + + memset(encoder._iv, 0, sizeof(encoder._iv)); + memset(encoder._key, 0, sizeof(encoder._key)); + memset(encoder._psw, 0, sizeof(encoder._psw)); + for (UInt32 j = 0; j < numSubDecoderThreads; j++) { - RINOK(CreateCoder(EXTERNAL_CODECS_LOC_VARS methodId, encoder.decoders[j], false)); - if (!encoder.decoders[j]) + CCreatedCoder cod; + CMyComPtr<ICompressCoder> &decoder = encoder._decoders[j]; + RINOK(CreateCoder(EXTERNAL_CODECS_LOC_VARS methodId, false, encoder._decoderFilter, cod)); + decoder = cod.Coder; + if (!encoder._decoderFilter && !decoder) return E_NOTIMPL; } } CBaseRandomGenerator rg; rg.Init(); + + UInt32 crc = 0; + if (fileData) + crc = CrcCalc(fileData, uncompressedDataSize); + for (i = 0; i < numEncoderThreads; i++) { - RINOK(encoders[i].Init(method, uncompressedDataSize, generateDictBits, &rg)); + CEncoderInfo &encoder = encoders[i]; + encoder._method = method; + encoder._uncompressedDataSize = uncompressedDataSize; + encoder.kBufferSize = uncompressedDataSize; + encoder.fileData = fileData; + encoder.crc = crc; + + RINOK(encoders[i].Init(method, generateDictBits, &rg)); } CBenchProgressStatus status; @@ -857,11 +1275,16 @@ static HRESULT MethodBench( for (i = 0; i < numEncoderThreads; i++) { CEncoderInfo &encoder = encoders[i]; + encoder.NumIterations = GetNumIterations(benchProps->GeComprCommands(uncompressedDataSize), complexInCommands); + for (int j = 0; j < 2; j++) { - encoder.progressInfo[j] = encoder.progressInfoSpec[j] = new CBenchProgressInfo; - encoder.progressInfoSpec[j]->Status = &status; + CBenchProgressInfo *spec = new CBenchProgressInfo; + encoder.progressInfoSpec[j] = spec; + encoder.progressInfo[j] = spec; + spec->Status = &status; } + if (i == 0) { CBenchProgressInfo *bpi = encoder.progressInfoSpec[0]; @@ -876,6 +1299,7 @@ static HRESULT MethodBench( #ifdef USE_ALLOCA encoder.AllocaSize = (i * 16 * 21) & 0x7FF; #endif + RINOK(encoder.CreateEncoderThread()) } else @@ -884,6 +1308,7 @@ static HRESULT MethodBench( RINOK(encoder.Encode()); } } + #ifndef _7ZIP_ST if (numEncoderThreads > 1) for (i = 0; i < numEncoderThreads; i++) @@ -897,13 +1322,15 @@ static HRESULT MethodBench( encoders[0].progressInfoSpec[0]->SetFinishTime(info); info.UnpackSize = 0; info.PackSize = 0; - info.NumIterations = 1; // progressInfoSpec->NumIterations; + info.NumIterations = encoders[0].NumIterations; + for (i = 0; i < numEncoderThreads; i++) { CEncoderInfo &encoder = encoders[i]; info.UnpackSize += encoder.kBufferSize; info.PackSize += encoder.compressedSize; } + RINOK(callback->SetEncodeResult(info, true)); @@ -911,14 +1338,14 @@ static HRESULT MethodBench( status.EncodeMode = false; UInt32 numDecoderThreads = numEncoderThreads * numSubDecoderThreads; + for (i = 0; i < numEncoderThreads; i++) { CEncoderInfo &encoder = encoders[i]; if (i == 0) { - encoder.NumIterations = (UInt32)(1 + kUncompressMinBlockSize / - benchProps->GeDecomprCommands(encoder.compressedSize, encoder.kBufferSize)); + encoder.NumIterations = GetNumIterations(benchProps->GeDecomprCommands(encoder.compressedSize, encoder.kBufferSize), complexInCommands); CBenchProgressInfo *bpi = encoder.progressInfoSpec[0]; bpi->Callback = callback; bpi->BenchInfo.NumIterations = numDecoderThreads; @@ -950,6 +1377,7 @@ static HRESULT MethodBench( RINOK(encoder.Decode(0)); } } + #ifndef _7ZIP_ST HRESULT res = S_OK; if (numDecoderThreads > 1) @@ -963,8 +1391,10 @@ static HRESULT MethodBench( } RINOK(res); #endif + RINOK(status.Res); encoders[0].progressInfoSpec[0]->SetFinishTime(info); + #ifndef _7ZIP_ST #ifdef UNDER_CE if (numDecoderThreads > 1) @@ -977,22 +1407,26 @@ static HRESULT MethodBench( } #endif #endif + info.UnpackSize = 0; info.PackSize = 0; info.NumIterations = numSubDecoderThreads * encoders[0].NumIterations; + for (i = 0; i < numEncoderThreads; i++) { CEncoderInfo &encoder = encoders[i]; info.UnpackSize += encoder.kBufferSize; info.PackSize += encoder.compressedSize; } + RINOK(callback->SetDecodeResult(info, false)); RINOK(callback->SetDecodeResult(info, true)); + return S_OK; } -inline UInt64 GetLZMAUsage(bool multiThread, UInt32 dictionary) +static inline UInt64 GetLZMAUsage(bool multiThread, UInt32 dictionary) { UInt32 hs = dictionary - 1; hs |= (hs >> 1); @@ -1008,33 +1442,142 @@ inline UInt64 GetLZMAUsage(bool multiThread, UInt32 dictionary) (1 << 20) + (multiThread ? (6 << 20) : 0); } -UInt64 GetBenchMemoryUsage(UInt32 numThreads, UInt32 dictionary) +UInt64 GetBenchMemoryUsage(UInt32 numThreads, UInt32 dictionary, bool totalBench) { const UInt32 kBufferSize = dictionary; - const UInt32 kCompressedBufferSize = (kBufferSize / 2); - UInt32 numSubThreads = (numThreads > 1) ? 2 : 1; - UInt32 numBigThreads = numThreads / numSubThreads; - return (kBufferSize + kCompressedBufferSize + - GetLZMAUsage((numThreads > 1), dictionary) + (2 << 20)) * numBigThreads; + const UInt32 kCompressedBufferSize = kBufferSize; // / 2; + bool lzmaMt = (totalBench || numThreads > 1); + UInt32 numBigThreads = numThreads; + if (!totalBench && lzmaMt) + numBigThreads /= 2; + return ((UInt64)kBufferSize + kCompressedBufferSize + + GetLZMAUsage(lzmaMt, dictionary) + (2 << 20)) * numBigThreads; } -static bool CrcBig(const void *data, UInt32 size, UInt32 numCycles, UInt32 crcBase) +static HRESULT CrcBig(const void *data, UInt32 size, UInt64 numIterations, + const UInt32 *checkSum, IHasher *hf, + IBenchPrintCallback *callback) { - for (UInt32 i = 0; i < numCycles; i++) - if (CrcCalc(data, size) != crcBase) - return false; - return true; + Byte hash[64]; + UInt64 i; + for (i = 0; i < sizeof(hash); i++) + hash[i] = 0; + for (i = 0; i < numIterations; i++) + { + if (callback && (i & 0xFF) == 0) + { + RINOK(callback->CheckBreak()); + } + hf->Init(); + hf->Update(data, size); + hf->Final(hash); + UInt32 hashSize = hf->GetDigestSize(); + if (hashSize > sizeof(hash)) + return S_FALSE; + UInt32 sum = 0; + for (UInt32 j = 0; j < hashSize; j += 4) + sum ^= GetUi32(hash + j); + if (checkSum && sum != *checkSum) + { + return S_FALSE; + } + } + return S_OK; +} + +UInt32 g_BenchCpuFreqTemp = 1; + +#define YY1 sum += val; sum ^= val; +#define YY3 YY1 YY1 YY1 YY1 +#define YY5 YY3 YY3 YY3 YY3 +#define YY7 YY5 YY5 YY5 YY5 +static const UInt32 kNumFreqCommands = 128; + +EXTERN_C_BEGIN + +static UInt32 CountCpuFreq(UInt32 sum, UInt32 num, UInt32 val) +{ + for (UInt32 i = 0; i < num; i++) + { + YY7 + } + return sum; } +EXTERN_C_END + + #ifndef _7ZIP_ST + +struct CFreqInfo +{ + NWindows::CThread Thread; + IBenchPrintCallback *Callback; + HRESULT CallbackRes; + UInt32 ValRes; + UInt32 Size; + UInt64 NumIterations; + + void Wait() + { + Thread.Wait(); + Thread.Close(); + } +}; + +static THREAD_FUNC_DECL FreqThreadFunction(void *param) +{ + CFreqInfo *p = (CFreqInfo *)param; + + UInt32 sum = g_BenchCpuFreqTemp; + for (UInt64 k = p->NumIterations; k > 0; k--) + { + p->CallbackRes = p->Callback->CheckBreak(); + if (p->CallbackRes != S_OK) + return 0; + sum = CountCpuFreq(sum, p->Size, g_BenchCpuFreqTemp); + } + p->ValRes = sum; + return 0; +} + +struct CFreqThreads +{ + CFreqInfo *Items; + UInt32 NumThreads; + + CFreqThreads(): Items(0), NumThreads(0) {} + void WaitAll() + { + for (UInt32 i = 0; i < NumThreads; i++) + Items[i].Wait(); + NumThreads = 0; + } + ~CFreqThreads() + { + WaitAll(); + delete []Items; + } +}; + struct CCrcInfo { NWindows::CThread Thread; + IBenchPrintCallback *Callback; + HRESULT CallbackRes; + const Byte *Data; UInt32 Size; - UInt32 NumCycles; - UInt32 Crc; - bool Res; + UInt64 NumIterations; + bool CheckSumDefined; + UInt32 CheckSum; + CMyComPtr<IHasher> Hasher; + HRESULT Res; + + #ifdef USE_ALLOCA + size_t AllocaSize; + #endif + void Wait() { Thread.Wait(); @@ -1045,14 +1588,22 @@ struct CCrcInfo static THREAD_FUNC_DECL CrcThreadFunction(void *param) { CCrcInfo *p = (CCrcInfo *)param; - p->Res = CrcBig(p->Data, p->Size, p->NumCycles, p->Crc); + + #ifdef USE_ALLOCA + alloca(p->AllocaSize); + #endif + + p->Res = CrcBig(p->Data, p->Size, p->NumIterations, + p->CheckSumDefined ? &p->CheckSum : NULL, p->Hasher, + p->Callback); return 0; } struct CCrcThreads { - UInt32 NumThreads; CCrcInfo *Items; + UInt32 NumThreads; + CCrcThreads(): Items(0), NumThreads(0) {} void WaitAll() { @@ -1066,6 +1617,7 @@ struct CCrcThreads delete []Items; } }; + #endif static UInt32 CrcCalc1(const Byte *buf, UInt32 size) @@ -1112,127 +1664,89 @@ bool CrcInternalTest() return true; } -static HRESULT CrcBench(UInt32 numThreads, UInt32 bufferSize, UInt64 &speed) +struct CBenchMethod { - if (numThreads == 0) - numThreads = 1; + unsigned Weight; + unsigned DictBits; + UInt32 EncComplex; + UInt32 DecComplexCompr; + UInt32 DecComplexUnc; + const char *Name; +}; - CBenchBuffer buffer; - size_t totalSize = (size_t)bufferSize * numThreads; - if (totalSize / numThreads != bufferSize) - return E_OUTOFMEMORY; - if (!buffer.Alloc(totalSize)) - return E_OUTOFMEMORY; +static const CBenchMethod g_Bench[] = +{ + { 40, 17, 357, 145, 20, "LZMA:x1" }, + { 80, 24, 1220, 145, 20, "LZMA:x5:mt1" }, + { 80, 24, 1220, 145, 20, "LZMA:x5:mt2" }, - Byte *buf = buffer.Buffer; - CBaseRandomGenerator RG; - UInt32 numCycles = (kCrcBlockSize) / ((bufferSize >> 2) + 1) + 1; + { 10, 16, 124, 40, 14, "Deflate:x1" }, + { 20, 16, 376, 40, 14, "Deflate:x5" }, + { 10, 16, 1082, 40, 14, "Deflate:x7" }, + { 10, 17, 422, 40, 14, "Deflate64:x5" }, - UInt64 timeVal; - #ifndef _7ZIP_ST - CCrcThreads threads; - if (numThreads > 1) - { - threads.Items = new CCrcInfo[numThreads]; - UInt32 i; - for (i = 0; i < numThreads; i++) - { - CCrcInfo &info = threads.Items[i]; - Byte *data = buf + (size_t)bufferSize * i; - info.Data = data; - info.NumCycles = numCycles; - info.Size = bufferSize; - info.Crc = RandGenCrc(data, bufferSize, RG); - } - timeVal = GetTimeCount(); - for (i = 0; i < numThreads; i++) - { - CCrcInfo &info = threads.Items[i]; - RINOK(info.Thread.Create(CrcThreadFunction, &info)); - threads.NumThreads++; - } - threads.WaitAll(); - for (i = 0; i < numThreads; i++) - if (!threads.Items[i].Res) - return S_FALSE; - } - else - #endif - { - UInt32 crc = RandGenCrc(buf, bufferSize, RG); - timeVal = GetTimeCount(); - if (!CrcBig(buf, bufferSize, numCycles, crc)) - return S_FALSE; - } - timeVal = GetTimeCount() - timeVal; - if (timeVal == 0) - timeVal = 1; + { 10, 15, 590, 69, 69, "BZip2:x1" }, + { 20, 19, 815, 122, 122, "BZip2:x5" }, + { 10, 19, 815, 122, 122, "BZip2:x5:mt2" }, + { 10, 19, 2530, 122, 122, "BZip2:x7" }, - UInt64 size = (UInt64)numCycles * totalSize; - speed = MyMultDiv64(size, timeVal, GetFreq()); - return S_OK; -} + { 10, 18, 1010, 0, 1150, "PPMD:x1" }, + { 10, 22, 1655, 0, 1830, "PPMD:x5" }, -struct CBenchMethod + { 2, 0, 6, 0, 6, "Delta:4" }, + { 2, 0, 4, 0, 4, "BCJ" }, + + { 10, 0, 24, 0, 24, "AES256CBC:1" }, + { 2, 0, 8, 0, 2, "AES256CBC:2" } +}; + +struct CBenchHash { - unsigned dictBits; - UInt32 EncComplex; - UInt32 DecComplexCompr; - UInt32 DecComplexUnc; + unsigned Weight; + UInt32 Complex; + UInt32 CheckSum; const char *Name; }; -static const CBenchMethod g_Bench[] = +static const CBenchHash g_Hash[] = { - { 17, 340, 155, 20, "LZMA:x1" }, - { 24, 1182, 155, 20, "LZMA:x5:mt1" }, - { 24, 1182, 155, 20, "LZMA:x5:mt2" }, - { 16, 124, 47, 14, "Deflate:x1" }, - { 16, 376, 47, 14, "Deflate:x5" }, - { 16, 1084, 47, 14, "Deflate:x7" }, - { 17, 420, 47, 14, "Deflate64:x5" }, - { 15, 590, 69, 70, "BZip2:x1" }, - { 19, 792, 119, 119, "BZip2:x5" }, - #ifndef UNDER_CE - { 19, 792, 119, 119, "BZip2:x5:mt2" }, - #endif - { 19, 2500, 118, 118, "BZip2:x7" }, - { 18, 1010, 0, 1155, "PPMD:x1" }, - { 22, 1650, 0, 1830, "PPMD:x5" } + { 1, 1820, 0x8F8FEDAB, "CRC32:1" }, + { 10, 558, 0x8F8FEDAB, "CRC32:4" }, + { 10, 339, 0x8F8FEDAB, "CRC32:8" }, + { 10, 512, 0xDF1C17CC, "CRC64" }, + { 10, 5100, 0x2D79FF2E, "SHA256" }, + { 10, 2340, 0x4C25132B, "SHA1" }, + { 2, 5500, 0xE084E913, "BLAKE2sp" } }; struct CTotalBenchRes { - UInt64 NumIterations; + // UInt64 NumIterations1; // for Usage + UInt64 NumIterations2; // for Rating / RPU + UInt64 Rating; UInt64 Usage; UInt64 RPU; - void Init() { NumIterations = 0; Rating = 0; Usage = 0; RPU = 0; } - void Normalize() - { - if (NumIterations == 0) - return; - Rating /= NumIterations; - Usage /= NumIterations; - RPU /= NumIterations; - NumIterations = 1; - } - void SetMid(const CTotalBenchRes &r1, const CTotalBenchRes &r2) + + void Init() { /* NumIterations1 = 0; */ NumIterations2 = 0; Rating = 0; Usage = 0; RPU = 0; } + + void SetSum(const CTotalBenchRes &r1, const CTotalBenchRes &r2) { - Rating = (r1.Rating + r2.Rating) / 2; - Usage = (r1.Usage + r2.Usage) / 2; - RPU = (r1.RPU + r2.RPU) / 2; - NumIterations = (r1.NumIterations + r2.NumIterations) / 2; + Rating = (r1.Rating + r2.Rating); + Usage = (r1.Usage + r2.Usage); + RPU = (r1.RPU + r2.RPU); + // NumIterations1 = (r1.NumIterations1 + r2.NumIterations1); + NumIterations2 = (r1.NumIterations2 + r2.NumIterations2); } }; -static void PrintNumber(IBenchPrintCallback &f, UInt64 value, int size, bool withSpace = true) +static void PrintNumber(IBenchPrintCallback &f, UInt64 value, int size) { char s[128]; int startPos = (int)sizeof(s) - 32; memset(s, ' ', startPos); ConvertUInt64ToString(value, s + startPos); - if (withSpace) + // if (withSpace) { startPos--; size++; @@ -1247,45 +1761,115 @@ static void PrintNumber(IBenchPrintCallback &f, UInt64 value, int size, bool wit f.Print(s + startPos); } -static void PrintRating(IBenchPrintCallback &f, UInt64 rating) +static const int kFieldSize_Name = 12; +static const int kFieldSize_SmallName = 4; +static const int kFieldSize_Speed = 9; +static const int kFieldSize_Usage = 5; +static const int kFieldSize_RU = 6; +static const int kFieldSize_Rating = 6; +static const int kFieldSize_EU = 5; +static const int kFieldSize_Effec = 5; + +static const int kFieldSize_TotalSize = 4 + kFieldSize_Speed + kFieldSize_Usage + kFieldSize_RU + kFieldSize_Rating; +static const int kFieldSize_EUAndEffec = 2 + kFieldSize_EU + kFieldSize_Effec; + + +static void PrintRating(IBenchPrintCallback &f, UInt64 rating, int size) { - PrintNumber(f, rating / 1000000, 6); + PrintNumber(f, (rating + 500000) / 1000000, size); } -static void PrintResults(IBenchPrintCallback &f, UInt64 usage, UInt64 rpu, UInt64 rating) + +static void PrintPercents(IBenchPrintCallback &f, UInt64 val, UInt64 divider, int size) { - PrintNumber(f, (usage + 5000) / 10000, 5); - PrintRating(f, rpu); - PrintRating(f, rating); + PrintNumber(f, (val * 100 + divider / 2) / divider, size); } -static void PrintResults(IBenchPrintCallback &f, const CBenchInfo &info, UInt64 rating, CTotalBenchRes &res) +static void PrintChars(IBenchPrintCallback &f, char c, unsigned size) { - UInt64 speed = MyMultDiv64(info.UnpackSize, info.GlobalTime, info.GlobalFreq); - PrintNumber(f, speed / 1024, 7); + char s[256]; + memset(s, (Byte)c, size); + s[size] = 0; + f.Print(s); +} + +static void PrintSpaces(IBenchPrintCallback &f, unsigned size) +{ + PrintChars(f, ' ', size); +} + +static void PrintResults(IBenchPrintCallback &f, UInt64 usage, UInt64 rpu, UInt64 rating, bool showFreq, UInt64 cpuFreq) +{ + PrintNumber(f, (usage + 5000) / 10000, kFieldSize_Usage); + PrintRating(f, rpu, kFieldSize_RU); + PrintRating(f, rating, kFieldSize_Rating); + if (showFreq) + { + if (cpuFreq == 0) + PrintSpaces(f, kFieldSize_EUAndEffec); + else + { + UInt64 ddd = cpuFreq * usage / 100; + if (ddd == 0) + ddd = 1; + PrintPercents(f, (rating * 10000), ddd, kFieldSize_EU); + PrintPercents(f, rating, cpuFreq, kFieldSize_Effec); + } + } +} + +static void PrintResults(IBenchPrintCallback *f, + const CBenchInfo &info, + unsigned weight, + UInt64 rating, + bool showFreq, UInt64 cpuFreq, + CTotalBenchRes *res) +{ + UInt64 speed = info.GetSpeed(info.UnpackSize * info.NumIterations); + if (f) + { + if (speed != 0) + PrintNumber(*f, speed / 1024, kFieldSize_Speed); + else + PrintSpaces(*f, 1 + kFieldSize_Speed); + } UInt64 usage = info.GetUsage(); UInt64 rpu = info.GetRatingPerUsage(rating); - PrintResults(f, usage, rpu, rating); - res.NumIterations++; - res.RPU += rpu; - res.Rating += rating; - res.Usage += usage; + if (f) + { + PrintResults(*f, usage, rpu, rating, showFreq, cpuFreq); + } + + if (res) + { + // res->NumIterations1++; + res->NumIterations2 += weight; + res->RPU += (rpu * weight); + res->Rating += (rating * weight); + res->Usage += (usage * weight); + } } -static void PrintTotals(IBenchPrintCallback &f, const CTotalBenchRes &res) +static void PrintTotals(IBenchPrintCallback &f, bool showFreq, UInt64 cpuFreq, const CTotalBenchRes &res) { - f.Print(" "); - PrintResults(f, res.Usage, res.RPU, res.Rating); + PrintSpaces(f, 1 + kFieldSize_Speed); + // UInt64 numIterations1 = res.NumIterations1; if (numIterations1 == 0) numIterations1 = 1; + UInt64 numIterations2 = res.NumIterations2; if (numIterations2 == 0) numIterations2 = 1; + PrintResults(f, res.Usage / numIterations2, res.RPU / numIterations2, res.Rating / numIterations2, showFreq, cpuFreq); } -static void PrintRequirements(IBenchPrintCallback &f, const char *sizeString, UInt64 size, const char *threadsString, UInt32 numThreads) +static void PrintRequirements(IBenchPrintCallback &f, const char *sizeString, + bool size_Defined, UInt64 size, const char *threadsString, UInt32 numThreads) { f.Print("RAM "); f.Print(sizeString); - PrintNumber(f, (size >> 20), 5, true); + if (size_Defined) + PrintNumber(f, (size >> 20), 6); + else + f.Print(" ?"); f.Print(" MB, # "); f.Print(threadsString); - PrintNumber(f, numThreads, 3, true); + PrintNumber(f, numThreads, 3); f.NewLine(); } @@ -1297,41 +1881,74 @@ struct CBenchCallbackToPrint: public IBenchCallback IBenchPrintCallback *_file; UInt32 DictSize; + bool Use2Columns; + int NameFieldSize; + + bool ShowFreq; + UInt64 CpuFreq; + + unsigned EncodeWeight; + unsigned DecodeWeight; + + CBenchCallbackToPrint(): + Use2Columns(false), + NameFieldSize(0), + ShowFreq(false), + CpuFreq(0), + EncodeWeight(1), + DecodeWeight(1) + {} + void Init() { EncodeRes.Init(); DecodeRes.Init(); } - void Normalize() { EncodeRes.Normalize(); DecodeRes.Normalize(); } + void Print(const char *s); + void NewLine(); + + HRESULT SetFreq(bool showFreq, UInt64 cpuFreq); HRESULT SetEncodeResult(const CBenchInfo &info, bool final); HRESULT SetDecodeResult(const CBenchInfo &info, bool final); - void Print(const char *string); - void NewLine(); - void PrintLeftAligned(const char *string, unsigned size); }; +HRESULT CBenchCallbackToPrint::SetFreq(bool showFreq, UInt64 cpuFreq) +{ + ShowFreq = showFreq; + CpuFreq = cpuFreq; + return S_OK; +} + HRESULT CBenchCallbackToPrint::SetEncodeResult(const CBenchInfo &info, bool final) { RINOK(_file->CheckBreak()); if (final) { - UInt64 rating = BenchProps.GetCompressRating(DictSize, info.GlobalTime, info.GlobalFreq, info.UnpackSize); - PrintResults(*_file, info, rating, EncodeRes); + UInt64 rating = BenchProps.GetCompressRating(DictSize, info.GlobalTime, info.GlobalFreq, info.UnpackSize * info.NumIterations); + PrintResults(_file, info, + EncodeWeight, rating, + ShowFreq, CpuFreq, &EncodeRes); + if (!Use2Columns) + _file->NewLine(); } return S_OK; } static const char *kSep = " | "; - HRESULT CBenchCallbackToPrint::SetDecodeResult(const CBenchInfo &info, bool final) { RINOK(_file->CheckBreak()); if (final) { UInt64 rating = BenchProps.GetDecompressRating(info.GlobalTime, info.GlobalFreq, info.UnpackSize, info.PackSize, info.NumIterations); - _file->Print(kSep); + if (Use2Columns) + _file->Print(kSep); + else + PrintSpaces(*_file, NameFieldSize); CBenchInfo info2 = info; info2.UnpackSize *= info2.NumIterations; info2.PackSize *= info2.NumIterations; info2.NumIterations = 1; - PrintResults(*_file, info2, rating, DecodeRes); + PrintResults(_file, info2, + DecodeWeight, rating, + ShowFreq, CpuFreq, &DecodeRes); } return S_OK; } @@ -1346,43 +1963,355 @@ void CBenchCallbackToPrint::NewLine() _file->NewLine(); } -void CBenchCallbackToPrint::PrintLeftAligned(const char *s, unsigned size) +void PrintLeft(IBenchPrintCallback &f, const char *s, unsigned size) { - AString s2 = s; - for (unsigned len = (unsigned)strlen(s); len < size; len++) - s2 += ' '; - Print(s2); + f.Print(s); + int numSpaces = size - MyStringLen(s); + if (numSpaces > 0) + PrintSpaces(f, numSpaces); +} + +void PrintRight(IBenchPrintCallback &f, const char *s, unsigned size) +{ + int numSpaces = size - MyStringLen(s); + if (numSpaces > 0) + PrintSpaces(f, numSpaces); + f.Print(s); } static HRESULT TotalBench( - DECL_EXTERNAL_CODECS_LOC_VARS - UInt32 numThreads, UInt32 unpackSize, IBenchPrintCallback *printCallback, CBenchCallbackToPrint *callback) + DECL_EXTERNAL_CODECS_LOC_VARS + UInt64 complexInCommands, + UInt32 numThreads, + bool forceUnpackSize, + size_t unpackSize, + const Byte *fileData, + IBenchPrintCallback *printCallback, CBenchCallbackToPrint *callback) { - for (unsigned i = 0; i < sizeof(g_Bench) / sizeof(g_Bench[0]); i++) + for (unsigned i = 0; i < ARRAY_SIZE(g_Bench); i++) { - CBenchMethod bench = g_Bench[i]; - callback->PrintLeftAligned(bench.Name, 12); + const CBenchMethod &bench = g_Bench[i]; + PrintLeft(*callback->_file, bench.Name, kFieldSize_Name); callback->BenchProps.DecComplexUnc = bench.DecComplexUnc; callback->BenchProps.DecComplexCompr = bench.DecComplexCompr; callback->BenchProps.EncComplex = bench.EncComplex; + COneMethodInfo method; NCOM::CPropVariant propVariant; propVariant = bench.Name; - RINOK(method.ParseMethodFromPROPVARIANT(L"", propVariant)); + RINOK(method.ParseMethodFromPROPVARIANT(UString(), propVariant)); + + size_t unpackSize2 = unpackSize; + if (!forceUnpackSize && bench.DictBits == 0) + unpackSize2 = kFilterUnpackSize; + + callback->EncodeWeight = bench.Weight; + callback->DecodeWeight = bench.Weight; HRESULT res = MethodBench( EXTERNAL_CODECS_LOC_VARS - false, numThreads, method, unpackSize, bench.dictBits, + complexInCommands, + false, numThreads, method, + unpackSize2, fileData, + bench.DictBits, printCallback, callback, &callback->BenchProps); + if (res == E_NOTIMPL) - callback->Print(" ---"); + { + // callback->Print(" ---"); + // we need additional empty line as line for decompression results + if (!callback->Use2Columns) + callback->NewLine(); + } else { RINOK(res); } + callback->NewLine(); } - return S_OK; + return S_OK; +} + + +static HRESULT FreqBench( + UInt64 complexInCommands, + UInt32 numThreads, + IBenchPrintCallback *_file, + bool showFreq, + UInt64 specifiedFreq, + UInt64 &cpuFreq, + UInt32 &res) +{ + res = 0; + cpuFreq = 0; + + UInt32 bufferSize = 1 << 20; + UInt32 complexity = kNumFreqCommands; + if (numThreads == 0) + numThreads = 1; + + #ifdef _7ZIP_ST + numThreads = 1; + #endif + + UInt32 bsize = (bufferSize == 0 ? 1 : bufferSize); + UInt64 numIterations = complexInCommands / complexity / bsize; + if (numIterations == 0) + numIterations = 1; + + CBenchInfoCalc progressInfoSpec; + + #ifndef _7ZIP_ST + CFreqThreads threads; + if (numThreads > 1) + { + threads.Items = new CFreqInfo[numThreads]; + UInt32 i; + for (i = 0; i < numThreads; i++) + { + CFreqInfo &info = threads.Items[i]; + info.Callback = _file; + info.CallbackRes = S_OK; + info.NumIterations = numIterations; + info.Size = bufferSize; + } + progressInfoSpec.SetStartTime(); + for (i = 0; i < numThreads; i++) + { + CFreqInfo &info = threads.Items[i]; + RINOK(info.Thread.Create(FreqThreadFunction, &info)); + threads.NumThreads++; + } + threads.WaitAll(); + for (i = 0; i < numThreads; i++) + { + RINOK(threads.Items[i].CallbackRes); + } + } + else + #endif + { + progressInfoSpec.SetStartTime(); + UInt32 sum = g_BenchCpuFreqTemp; + for (UInt64 k = numIterations; k > 0; k--) + { + RINOK(_file->CheckBreak()); + sum = CountCpuFreq(sum, bufferSize, g_BenchCpuFreqTemp); + } + res += sum; + } + + CBenchInfo info; + progressInfoSpec.SetFinishTime(info); + + info.UnpackSize = 0; + info.PackSize = 0; + info.NumIterations = 1; + + if (_file) + { + { + UInt64 numCommands = (UInt64)numIterations * bufferSize * numThreads * complexity; + UInt64 rating = info.GetSpeed(numCommands); + cpuFreq = rating / numThreads; + PrintResults(_file, info, + 0, // weight + rating, + showFreq, showFreq ? (specifiedFreq != 0 ? specifiedFreq : cpuFreq) : 0, NULL); + } + RINOK(_file->CheckBreak()); + } + + return S_OK; +} + + + +static HRESULT CrcBench( + DECL_EXTERNAL_CODECS_LOC_VARS + UInt64 complexInCommands, + UInt32 numThreads, UInt32 bufferSize, + UInt64 &speed, + UInt32 complexity, unsigned benchWeight, + const UInt32 *checkSum, + const COneMethodInfo &method, + IBenchPrintCallback *_file, + CTotalBenchRes *encodeRes, + bool showFreq, UInt64 cpuFreq) +{ + if (numThreads == 0) + numThreads = 1; + + #ifdef _7ZIP_ST + numThreads = 1; + #endif + + AString methodName = method.MethodName; + // methodName.RemoveChar(L'-'); + CMethodId hashID; + if (!FindHashMethod( + EXTERNAL_CODECS_LOC_VARS + methodName, hashID)) + return E_NOTIMPL; + + CBenchBuffer buffer; + size_t totalSize = (size_t)bufferSize * numThreads; + if (totalSize / numThreads != bufferSize) + return E_OUTOFMEMORY; + if (!buffer.Alloc(totalSize)) + return E_OUTOFMEMORY; + + Byte *buf = buffer.Buffer; + CBaseRandomGenerator RG; + UInt32 bsize = (bufferSize == 0 ? 1 : bufferSize); + UInt64 numIterations = complexInCommands * 256 / complexity / bsize; + if (numIterations == 0) + numIterations = 1; + + CBenchInfoCalc progressInfoSpec; + + #ifndef _7ZIP_ST + CCrcThreads threads; + if (numThreads > 1) + { + threads.Items = new CCrcInfo[numThreads]; + + UInt32 i; + for (i = 0; i < numThreads; i++) + { + CCrcInfo &info = threads.Items[i]; + AString name; + RINOK(CreateHasher(EXTERNAL_CODECS_LOC_VARS hashID, name, info.Hasher)); + if (!info.Hasher) + return E_NOTIMPL; + CMyComPtr<ICompressSetCoderProperties> scp; + info.Hasher.QueryInterface(IID_ICompressSetCoderProperties, &scp); + if (scp) + { + UInt64 reduceSize = 1; + RINOK(method.SetCoderProps(scp, &reduceSize)); + } + + Byte *data = buf + (size_t)bufferSize * i; + info.Callback = _file; + info.Data = data; + info.NumIterations = numIterations; + info.Size = bufferSize; + /* info.Crc = */ RandGenCrc(data, bufferSize, RG); + info.CheckSumDefined = false; + if (checkSum) + { + info.CheckSum = *checkSum; + info.CheckSumDefined = (checkSum && (i == 0)); + } + + #ifdef USE_ALLOCA + info.AllocaSize = (i * 16 * 21) & 0x7FF; + #endif + } + + progressInfoSpec.SetStartTime(); + + for (i = 0; i < numThreads; i++) + { + CCrcInfo &info = threads.Items[i]; + RINOK(info.Thread.Create(CrcThreadFunction, &info)); + threads.NumThreads++; + } + threads.WaitAll(); + for (i = 0; i < numThreads; i++) + { + RINOK(threads.Items[i].Res); + } + } + else + #endif + { + /* UInt32 crc = */ RandGenCrc(buf, bufferSize, RG); + progressInfoSpec.SetStartTime(); + CMyComPtr<IHasher> hasher; + AString name; + RINOK(CreateHasher(EXTERNAL_CODECS_LOC_VARS hashID, name, hasher)); + if (!hasher) + return E_NOTIMPL; + CMyComPtr<ICompressSetCoderProperties> scp; + hasher.QueryInterface(IID_ICompressSetCoderProperties, &scp); + if (scp) + { + UInt64 reduceSize = 1; + RINOK(method.SetCoderProps(scp, &reduceSize)); + } + RINOK(CrcBig(buf, bufferSize, numIterations, checkSum, hasher, _file)); + } + + CBenchInfo info; + progressInfoSpec.SetFinishTime(info); + + UInt64 unpSize = numIterations * bufferSize; + UInt64 unpSizeThreads = unpSize * numThreads; + info.UnpackSize = unpSizeThreads; + info.PackSize = unpSizeThreads; + info.NumIterations = 1; + + if (_file) + { + { + UInt64 numCommands = unpSizeThreads * complexity / 256; + UInt64 rating = info.GetSpeed(numCommands); + PrintResults(_file, info, + benchWeight, rating, + showFreq, cpuFreq, encodeRes); + } + RINOK(_file->CheckBreak()); + } + + speed = info.GetSpeed(unpSizeThreads); + + return S_OK; +} + +static HRESULT TotalBench_Hash( + DECL_EXTERNAL_CODECS_LOC_VARS + UInt64 complexInCommands, + UInt32 numThreads, UInt32 bufSize, + IBenchPrintCallback *printCallback, CBenchCallbackToPrint *callback, + CTotalBenchRes *encodeRes, + bool showFreq, UInt64 cpuFreq) +{ + for (unsigned i = 0; i < ARRAY_SIZE(g_Hash); i++) + { + const CBenchHash &bench = g_Hash[i]; + PrintLeft(*callback->_file, bench.Name, kFieldSize_Name); + // callback->BenchProps.DecComplexUnc = bench.DecComplexUnc; + // callback->BenchProps.DecComplexCompr = bench.DecComplexCompr; + // callback->BenchProps.EncComplex = bench.EncComplex; + + COneMethodInfo method; + NCOM::CPropVariant propVariant; + propVariant = bench.Name; + RINOK(method.ParseMethodFromPROPVARIANT(UString(), propVariant)); + + UInt64 speed; + HRESULT res = CrcBench( + EXTERNAL_CODECS_LOC_VARS + complexInCommands, + numThreads, bufSize, + speed, + bench.Complex, bench.Weight, + &bench.CheckSum, method, + printCallback, encodeRes, showFreq, cpuFreq); + if (res == E_NOTIMPL) + { + // callback->Print(" ---"); + } + else + { + RINOK(res); + } + callback->NewLine(); + } + return S_OK; } struct CTempValues @@ -1392,23 +2321,130 @@ struct CTempValues ~CTempValues() { delete []Values; } }; -static void String_to_PropVariant(const UString &s, NCOM::CPropVariant &prop) +static void ParseNumberString(const UString &s, NCOM::CPropVariant &prop) { - const wchar_t *endPtr; - UInt64 result = ConvertStringToUInt64(s, &endPtr); - if (endPtr - (const wchar_t *)s != s.Length()) + const wchar_t *end; + UInt64 result = ConvertStringToUInt64(s, &end); + if (*end != 0 || s.IsEmpty()) prop = s; - else if (result <= 0xFFFFFFFF) + else if (result <= (UInt32)0xFFFFFFFF) prop = (UInt32)result; else prop = result; } +static UInt32 GetNumThreadsNext(unsigned i, UInt32 numThreads) +{ + if (i < 2) + return i + 1; + i -= 1; + UInt32 num = (UInt32)(2 + (i & 1)) << (i >> 1); + return (num <= numThreads) ? num : numThreads; +} + +static bool AreSameMethodNames(const char *fullName, const char *shortName) +{ + for (;;) + { + char c2 = *shortName++; + if (c2 == 0) + return true; + char c1 = *fullName++; + if (MyCharLower_Ascii(c1) != MyCharLower_Ascii(c2)) + return false; + } +} + + +#ifdef MY_CPU_X86_OR_AMD64 + +static void PrintCpuChars(AString &s, UInt32 v) +{ + for (int j = 0; j < 4; j++) + { + Byte b = (Byte)(v & 0xFF); + v >>= 8; + if (b == 0) + break; + s += (char)b; + } +} + +static void x86cpuid_to_String(const Cx86cpuid &c, AString &s) +{ + s.Empty(); + + UInt32 maxFunc2 = 0; + UInt32 t[3]; + + MyCPUID(0x80000000, &maxFunc2, &t[0], &t[1], &t[2]); + + bool fullNameIsAvail = (maxFunc2 >= 0x80000004); + + if (!fullNameIsAvail) + { + for (int i = 0; i < 3; i++) + PrintCpuChars(s, c.vendor[i]); + } + else + { + for (int i = 0; i < 3; i++) + { + UInt32 c[4] = { 0 }; + MyCPUID(0x80000002 + i, &c[0], &c[1], &c[2], &c[3]); + for (int j = 0; j < 4; j++) + PrintCpuChars(s, c[j]); + } + } + + s.Add_Space_if_NotEmpty(); + { + char temp[32]; + ConvertUInt32ToHex(c.ver, temp); + s += '('; + s += temp; + s += ')'; + } +} + +#endif + + +void GetCpuName(AString &s) +{ + s.Empty(); + + #ifdef MY_CPU_X86_OR_AMD64 + { + Cx86cpuid cpuid; + if (x86cpuid_CheckAndRead(&cpuid)) + { + x86cpuid_to_String(cpuid, s); + return; + } + #ifdef MY_CPU_AMD64 + s = "x64"; + #else + s = "x86"; + #endif + } + #else + + #ifdef MY_CPU_LE + s = "LE"; + #elif defined(MY_CPU_BE) + s = "BE"; + #endif + + #endif +} + + HRESULT Bench( DECL_EXTERNAL_CODECS_LOC_VARS IBenchPrintCallback *printCallback, IBenchCallback *benchCallback, - const CObjectVector<CProperty> props, + const CObjectVector<CProperty> &props, UInt32 numIterations, bool multiDict) { @@ -1416,43 +2452,205 @@ HRESULT Bench( return S_FALSE; UInt32 numCPUs = 1; - UInt64 ramSize = (UInt64)512 << 20; + UInt64 ramSize = (UInt64)(sizeof(size_t)) << 29; + #ifndef _7ZIP_ST numCPUs = NSystem::GetNumberOfProcessors(); #endif - #if !defined(_7ZIP_ST) || defined(_WIN32) - ramSize = NSystem::GetRamSize(); - #endif - UInt32 numThreads = numCPUs; + + bool ramSize_Defined = NSystem::GetRamSize(ramSize); - if (printCallback) - PrintRequirements(*printCallback, "size: ", ramSize, "CPU hardware threads:", numCPUs); + UInt32 numThreadsSpecified = numCPUs; + + UInt32 testTime = kComplexInSeconds; + + UInt64 specifiedFreq = 0; + + bool multiThreadTests = false; COneMethodInfo method; - int i; + unsigned i; + + CBenchBuffer fileDataBuffer; + for (i = 0; i < props.Size(); i++) { const CProperty &property = props[i]; - NCOM::CPropVariant propVariant; UString name = property.Name; - name.MakeUpper(); + name.MakeLower_Ascii(); + + if (name.IsEqualTo("file")) + { + if (property.Value.IsEmpty()) + return E_INVALIDARG; + + #ifdef USE_WIN_FILE + + NFile::NIO::CInFile file; + if (!file.Open(us2fs(property.Value))) + return E_INVALIDARG; + UInt64 len; + if (!file.GetLength(len)) + return E_FAIL; + if (len >= ((UInt32)1 << 31) || len == 0) + return E_INVALIDARG; + if (!fileDataBuffer.Alloc((size_t)len)) + return E_OUTOFMEMORY; + UInt32 processedSize; + file.Read(fileDataBuffer.Buffer, (UInt32)len, processedSize); + if (processedSize != len) + return E_FAIL; + if (printCallback) + { + printCallback->Print("file size ="); + // printCallback->Print(GetOemString(property.Value)); + PrintNumber(*printCallback, len, 0); + printCallback->NewLine(); + } + continue; + + #else + + return E_NOTIMPL; + + #endif + } + + NCOM::CPropVariant propVariant; if (!property.Value.IsEmpty()) - String_to_PropVariant(property.Value, propVariant); - if (name.Left(2).CompareNoCase(L"MT") == 0) + ParseNumberString(property.Value, propVariant); + + if (name.IsEqualTo("time")) + { + RINOK(ParsePropToUInt32(L"", propVariant, testTime)); + continue; + } + + if (name.IsEqualTo("freq")) + { + UInt32 freq32 = 0; + RINOK(ParsePropToUInt32(L"", propVariant, freq32)); + if (freq32 == 0) + return E_INVALIDARG; + specifiedFreq = (UInt64)freq32 * 1000000; + + if (printCallback) + { + printCallback->Print("freq="); + PrintNumber(*printCallback, freq32, 0); + printCallback->NewLine(); + } + + continue; + } + + if (name.IsPrefixedBy_Ascii_NoCase("mt")) { + UString s = name.Ptr(2); + if (s == L"*") + { + multiThreadTests = true; + continue; + } + if (s.IsEmpty() && propVariant.vt == VT_BSTR) + { + if (wcscmp(propVariant.bstrVal, L"*") == 0) + { + multiThreadTests = true; + continue; + } + } #ifndef _7ZIP_ST - RINOK(ParseMtProp(name.Mid(2), propVariant, numCPUs, numThreads)); + RINOK(ParseMtProp(s, propVariant, numCPUs, numThreadsSpecified)); #endif continue; } + RINOK(method.ParseMethodFromPROPVARIANT(name, propVariant)); } + if (printCallback) + { + AString s; + GetCpuName(s); + printCallback->Print(s); + printCallback->NewLine(); + } + + if (printCallback) + { + printCallback->Print("CPU Freq:"); + } + + UInt64 complexInCommands = kComplexInCommands; + + if (printCallback /* || benchCallback */) + { + UInt64 numMilCommands = 1 << 6; + if (specifiedFreq != 0) + { + while (numMilCommands > 1 && specifiedFreq < (numMilCommands * 1000000)) + numMilCommands >>= 1; + } + + for (int jj = 0;; jj++) + { + if (printCallback) + RINOK(printCallback->CheckBreak()); + + UInt64 start = ::GetTimeCount(); + UInt32 sum = (UInt32)start; + sum = CountCpuFreq(sum, (UInt32)(numMilCommands * 1000000 / kNumFreqCommands), g_BenchCpuFreqTemp); + const UInt64 realDelta = ::GetTimeCount() - start; + start = realDelta; + if (start == 0) + start = 1; + UInt64 freq = GetFreq(); + // mips is constant in some compilers + const UInt64 mipsVal = numMilCommands * freq / start; + if (printCallback) + { + if (realDelta == 0) + { + printCallback->Print(" -"); + } + else + { + // PrintNumber(*printCallback, start, 0); + PrintNumber(*printCallback, mipsVal, 5 + ((sum == 0xF1541213) ? 1 : 0)); + } + } + /* + if (benchCallback) + benchCallback->AddCpuFreq(mipsVal); + */ + + if (jj >= 3) + { + SetComplexCommands(testTime, false, mipsVal * 1000000, complexInCommands); + if (jj >= 8 || start >= freq) + break; + // break; // change it + numMilCommands <<= 1; + } + } + } + + if (printCallback) + { + printCallback->NewLine(); + printCallback->NewLine(); + PrintRequirements(*printCallback, "size: ", ramSize_Defined, ramSize, "CPU hardware threads:", numCPUs); + } + + if (numThreadsSpecified < 1 || numThreadsSpecified > kNumThreadsMax) + return E_INVALIDARG; + UInt32 dict; bool dictIsDefined = method.Get_DicSize(dict); if (method.MethodName.IsEmpty()) - method.MethodName = L"LZMA"; + method.MethodName = "LZMA"; if (benchCallback) { @@ -1462,12 +2660,20 @@ HRESULT Bench( UInt32 uncompressedDataSize = kAdditionalSize + dictSize; return MethodBench( EXTERNAL_CODECS_LOC_VARS - true, numThreads, - method, uncompressedDataSize, + complexInCommands, + true, numThreadsSpecified, + method, + uncompressedDataSize, fileDataBuffer.Buffer, kOldLzmaDictBits, printCallback, benchCallback, &benchProps); } - if (method.MethodName.CompareNoCase(L"CRC") == 0) + AString methodName = method.MethodName; + if (methodName.IsEqualTo_Ascii_NoCase("CRC")) + methodName = "crc32"; + method.MethodName = methodName; + CMethodId hashID; + + if (FindHashMethod(EXTERNAL_CODECS_LOC_VARS methodName, hashID)) { if (!printCallback) return S_FALSE; @@ -1475,33 +2681,80 @@ HRESULT Bench( if (!dictIsDefined) dict = (1 << 24); - CTempValues speedTotals(numThreads); + + // methhodName.RemoveChar(L'-'); + UInt32 complexity = 10000; + const UInt32 *checkSum = NULL; + { + for (unsigned i = 0; i < ARRAY_SIZE(g_Hash); i++) + { + const CBenchHash &h = g_Hash[i]; + AString s = h.Name; + AString hProp; + int propPos = s.Find(':'); + if (propPos >= 0) + { + hProp = s.Ptr(propPos + 1); + s.DeleteFrom(propPos); + } + + if (AreSameMethodNames(s, methodName)) + { + complexity = h.Complex; + checkSum = &h.CheckSum; + if (method.PropsString.IsEqualTo_Ascii_NoCase(hProp)) + break; + } + } + } + f.NewLine(); f.Print("Size"); - for (UInt32 ti = 0; ti < numThreads; ti++) + const int kFieldSize_CrcSpeed = 6; + unsigned numThreadsTests = 0; + for (;;) { - PrintNumber(f, ti + 1, 5); - speedTotals.Values[ti] = 0; + UInt32 t = GetNumThreadsNext(numThreadsTests, numThreadsSpecified); + PrintNumber(f, t, kFieldSize_CrcSpeed); + numThreadsTests++; + if (t >= numThreadsSpecified) + break; } f.NewLine(); f.NewLine(); + CTempValues speedTotals(numThreadsTests); + { + for (unsigned ti = 0; ti < numThreadsTests; ti++) + speedTotals.Values[ti] = 0; + } UInt64 numSteps = 0; for (UInt32 i = 0; i < numIterations; i++) { - for (int pow = 10; pow < 32; pow++) + for (unsigned pow = 10; pow < 32; pow++) { UInt32 bufSize = (UInt32)1 << pow; if (bufSize > dict) break; - PrintNumber(f, pow, 2, false); - f.Print(": "); - for (UInt32 ti = 0; ti < numThreads; ti++) + char s[16]; + ConvertUInt32ToString(pow, s); + unsigned pos = MyStringLen(s); + s[pos++] = ':'; + s[pos++] = ' '; + s[pos] = 0; + f.Print(s); + + for (unsigned ti = 0; ti < numThreadsTests; ti++) { RINOK(f.CheckBreak()); - UInt64 speed; - RINOK(CrcBench(ti + 1, bufSize, speed)); - PrintNumber(f, (speed >> 20), 5); + UInt32 t = GetNumThreadsNext(ti, numThreadsSpecified); + UInt64 speed = 0; + RINOK(CrcBench(EXTERNAL_CODECS_LOC_VARS complexInCommands, + t, bufSize, speed, + complexity, + 1, // benchWeight, + (pow == kNumHashDictBits) ? checkSum : NULL, method, NULL, NULL, false, 0)); + PrintNumber(f, (speed >> 20), kFieldSize_CrcSpeed); speedTotals.Values[ti] += speed; } f.NewLine(); @@ -1512,100 +2765,302 @@ HRESULT Bench( { f.NewLine(); f.Print("Avg:"); - for (UInt32 ti = 0; ti < numThreads; ti++) - PrintNumber(f, ((speedTotals.Values[ti] / numSteps) >> 20), 5); + for (unsigned ti = 0; ti < numThreadsTests; ti++) + { + PrintNumber(f, ((speedTotals.Values[ti] / numSteps) >> 20), kFieldSize_CrcSpeed); + } f.NewLine(); } return S_OK; } + bool use2Columns = false; + + bool totalBenchMode = (method.MethodName.IsEqualTo_Ascii_NoCase("*")); + bool onlyHashBench = false; + if (method.MethodName.IsEqualTo_Ascii_NoCase("hash")) + { + onlyHashBench = true; + totalBenchMode = true; + } + + // ---------- Threads loop ---------- + for (unsigned threadsPassIndex = 0; threadsPassIndex < 3; threadsPassIndex++) + { + + UInt32 numThreads = numThreadsSpecified; + + if (!multiThreadTests) + { + if (threadsPassIndex != 0) + break; + } + else + { + numThreads = 1; + if (threadsPassIndex != 0) + { + if (numCPUs < 2) + break; + numThreads = numCPUs; + if (threadsPassIndex == 1) + { + if (numCPUs >= 4) + numThreads = numCPUs / 2; + } + else if (numCPUs < 4) + break; + } + } + CBenchCallbackToPrint callback; callback.Init(); callback._file = printCallback; + + IBenchPrintCallback &f = *printCallback; + + if (threadsPassIndex > 0) + { + f.NewLine(); + f.NewLine(); + } if (!dictIsDefined) { - int dicSizeLog; - for (dicSizeLog = 25; dicSizeLog > kBenchMinDicLogSize; dicSizeLog--) - if (GetBenchMemoryUsage(numThreads, ((UInt32)1 << dicSizeLog)) + (8 << 20) <= ramSize) + const unsigned dicSizeLog_Main = (totalBenchMode ? 24 : 25); + unsigned dicSizeLog = dicSizeLog_Main; + + #ifdef UNDER_CE + dicSizeLog = (UInt64)1 << 20; + #endif + + if (ramSize_Defined) + for (; dicSizeLog > kBenchMinDicLogSize; dicSizeLog--) + if (GetBenchMemoryUsage(numThreads, ((UInt32)1 << dicSizeLog), totalBenchMode) + (8 << 20) <= ramSize) break; - dict = (1 << dicSizeLog); + + dict = (UInt32)1 << dicSizeLog; + + if (totalBenchMode && dicSizeLog != dicSizeLog_Main) + { + f.Print("Dictionary reduced to: "); + PrintNumber(f, dicSizeLog, 1); + f.NewLine(); + } } - IBenchPrintCallback &f = *printCallback; - PrintRequirements(f, "usage:", GetBenchMemoryUsage(numThreads, dict), "Benchmark threads: ", numThreads); + PrintRequirements(f, "usage:", true, GetBenchMemoryUsage(numThreads, dict, totalBenchMode), "Benchmark threads: ", numThreads); - bool totalBenchMode = (method.MethodName == L"*"); f.NewLine(); - f.Print(totalBenchMode ? "Method " : "Dict"); - f.Print(" Compressing | Decompressing"); + + if (totalBenchMode) + { + callback.NameFieldSize = kFieldSize_Name; + use2Columns = false; + } + else + { + callback.NameFieldSize = kFieldSize_SmallName; + use2Columns = true; + } + callback.Use2Columns = use2Columns; + + bool showFreq = false; + UInt64 cpuFreq = 0; + + if (totalBenchMode) + { + showFreq = true; + } + + int fileldSize = kFieldSize_TotalSize; + if (showFreq) + fileldSize += kFieldSize_EUAndEffec; + + if (use2Columns) + { + PrintSpaces(f, callback.NameFieldSize); + PrintRight(f, "Compressing", fileldSize); + f.Print(kSep); + PrintRight(f, "Decompressing", fileldSize); + } f.NewLine(); - const char *kSpaces = totalBenchMode ? " " : " "; - f.Print(kSpaces); + PrintLeft(f, totalBenchMode ? "Method" : "Dict", callback.NameFieldSize); + int j; - + for (j = 0; j < 2; j++) { - f.Print(" Speed Usage R/U Rating"); + PrintRight(f, "Speed", kFieldSize_Speed + 1); + PrintRight(f, "Usage", kFieldSize_Usage + 1); + PrintRight(f, "R/U", kFieldSize_RU + 1); + PrintRight(f, "Rating", kFieldSize_Rating + 1); + if (showFreq) + { + PrintRight(f, "E/U", kFieldSize_EU + 1); + PrintRight(f, "Effec", kFieldSize_Effec + 1); + } + if (!use2Columns) + break; if (j == 0) f.Print(kSep); } + f.NewLine(); - f.Print(kSpaces); + PrintSpaces(f, callback.NameFieldSize); + for (j = 0; j < 2; j++) { - f.Print(" KB/s % MIPS MIPS"); + PrintRight(f, "KiB/s", kFieldSize_Speed + 1); + PrintRight(f, "%", kFieldSize_Usage + 1); + PrintRight(f, "MIPS", kFieldSize_RU + 1); + PrintRight(f, "MIPS", kFieldSize_Rating + 1); + if (showFreq) + { + PrintRight(f, "%", kFieldSize_EU + 1); + PrintRight(f, "%", kFieldSize_Effec + 1); + } + if (!use2Columns) + break; if (j == 0) f.Print(kSep); } + f.NewLine(); f.NewLine(); + if (specifiedFreq != 0) + cpuFreq = specifiedFreq; + + if (totalBenchMode) { - if (!dictIsDefined) - dict = - #ifdef UNDER_CE - (UInt64)1 << 20; - #else - (UInt64)1 << 24; - #endif for (UInt32 i = 0; i < numIterations; i++) { if (i != 0) printCallback->NewLine(); - HRESULT res = TotalBench( - EXTERNAL_CODECS_LOC_VARS - numThreads, dict, printCallback, &callback); + HRESULT res; + + int freqTest; + const int kNumCpuTests = 3; + for (freqTest = 0; freqTest < kNumCpuTests; freqTest++) + { + PrintLeft(f, "CPU", kFieldSize_Name); + UInt32 resVal; + RINOK(FreqBench(complexInCommands, numThreads, printCallback, + (freqTest == kNumCpuTests - 1 || specifiedFreq != 0), // showFreq + specifiedFreq, + cpuFreq, resVal)); + callback.NewLine(); + + if (specifiedFreq != 0) + cpuFreq = specifiedFreq; + + if (freqTest == kNumCpuTests - 1) + SetComplexCommands(testTime, specifiedFreq != 0, cpuFreq, complexInCommands); + } + callback.NewLine(); + + callback.SetFreq(true, cpuFreq); + + if (!onlyHashBench) + { + res = TotalBench(EXTERNAL_CODECS_LOC_VARS + complexInCommands, numThreads, + dictIsDefined || fileDataBuffer.Buffer, // forceUnpackSize + fileDataBuffer.Buffer ? fileDataBuffer.BufferSize : dict, + fileDataBuffer.Buffer, + printCallback, &callback); + RINOK(res); + } + + res = TotalBench_Hash(EXTERNAL_CODECS_LOC_VARS complexInCommands, numThreads, + 1 << kNumHashDictBits, printCallback, &callback, &callback.EncodeRes, true, cpuFreq); RINOK(res); + + callback.NewLine(); + { + PrintLeft(f, "CPU", kFieldSize_Name); + UInt32 resVal; + UInt64 cpuFreqLastTemp = cpuFreq; + RINOK(FreqBench(complexInCommands, numThreads, printCallback, + specifiedFreq != 0, // showFreq + specifiedFreq, + cpuFreqLastTemp, resVal)); + callback.NewLine(); + } } } else { + bool needSetComplexity = true; + if (!methodName.IsEqualTo_Ascii_NoCase("LZMA")) + { + for (unsigned i = 0; i < ARRAY_SIZE(g_Bench); i++) + { + const CBenchMethod &h = g_Bench[i]; + AString s = h.Name; + if (AreSameMethodNames(h.Name, methodName)) + { + callback.BenchProps.EncComplex = h.EncComplex; + callback.BenchProps.DecComplexCompr = h.DecComplexCompr; + callback.BenchProps.DecComplexUnc = h.DecComplexUnc;; + needSetComplexity = false; + break; + } + } + } + if (needSetComplexity) + callback.BenchProps.SetLzmaCompexity(); - callback.BenchProps.SetLzmaCompexity(); - - for (i = 0; i < (int)numIterations; i++) + for (i = 0; i < numIterations; i++) { - const int kStartDicLog = 22; - int pow = (dict < ((UInt32)1 << kStartDicLog)) ? kBenchMinDicLogSize : kStartDicLog; + const unsigned kStartDicLog = 22; + unsigned pow = (dict < ((UInt32)1 << kStartDicLog)) ? kBenchMinDicLogSize : kStartDicLog; if (!multiDict) pow = 31; - while (((UInt32)1 << pow) > dict) + while (((UInt32)1 << pow) > dict && pow > 0) pow--; for (; ((UInt32)1 << pow) <= dict; pow++) { - PrintNumber(f, pow, 2, false); - f.Print(":"); + char s[16]; + ConvertUInt32ToString(pow, s); + unsigned pos = MyStringLen(s); + s[pos++] = ':'; + s[pos] = 0; + PrintLeft(f, s, kFieldSize_SmallName); callback.DictSize = (UInt32)1 << pow; - UInt32 uncompressedDataSize = kAdditionalSize + callback.DictSize; + COneMethodInfo method2 = method; + + if (StringsAreEqualNoCase_Ascii(method2.MethodName, "LZMA")) + { + // We add dictionary size property. + // method2 can have two different dictionary size properties. + // And last property is main. + NCOM::CPropVariant propVariant = (UInt32)pow; + RINOK(method2.ParseMethodFromPROPVARIANT(L"d", propVariant)); + } + + size_t uncompressedDataSize; + if (fileDataBuffer.Buffer) + { + uncompressedDataSize = fileDataBuffer.BufferSize; + } + else + { + uncompressedDataSize = callback.DictSize; + if (uncompressedDataSize >= (1 << 18)) + uncompressedDataSize += kAdditionalSize; + } HRESULT res = MethodBench( - EXTERNAL_CODECS_LOC_VARS - true, numThreads, - method, uncompressedDataSize, - kOldLzmaDictBits, printCallback, &callback, &callback.BenchProps); + EXTERNAL_CODECS_LOC_VARS + complexInCommands, + true, numThreads, + method2, + uncompressedDataSize, fileDataBuffer.Buffer, + kOldLzmaDictBits, printCallback, &callback, &callback.BenchProps); f.NewLine(); RINOK(res); if (!multiDict) @@ -1613,21 +3068,32 @@ HRESULT Bench( } } } - callback.Normalize(); - f.Print("----------------------------------------------------------------"); - f.NewLine(); - f.Print("Avr:"); - const char *kSpaces2 = totalBenchMode ? " " : ""; - f.Print(kSpaces2); - PrintTotals(f, callback.EncodeRes); - f.Print(" "); - PrintTotals(f, callback.DecodeRes); + + PrintChars(f, '-', callback.NameFieldSize + fileldSize); + + if (use2Columns) + { + f.Print(kSep); + PrintChars(f, '-', fileldSize); + } + f.NewLine(); - f.Print("Tot:"); - f.Print(kSpaces2); + + if (use2Columns) + { + PrintLeft(f, "Avr:", callback.NameFieldSize); + PrintTotals(f, showFreq, cpuFreq, callback.EncodeRes); + f.Print(kSep); + PrintTotals(f, showFreq, cpuFreq, callback.DecodeRes); + f.NewLine(); + } + + PrintLeft(f, "Tot:", callback.NameFieldSize); CTotalBenchRes midRes; - midRes.SetMid(callback.EncodeRes, callback.DecodeRes); - PrintTotals(f, midRes); + midRes.SetSum(callback.EncodeRes, callback.DecodeRes); + PrintTotals(f, showFreq, cpuFreq, midRes); f.NewLine(); + + } return S_OK; } diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/Bench.h b/3rdparty/lzma/CPP/7zip/UI/Common/Bench.h index 46146e24e1a..841250ef2cc 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/Bench.h +++ b/3rdparty/lzma/CPP/7zip/UI/Common/Bench.h @@ -14,25 +14,27 @@ struct CBenchInfo UInt64 UserFreq; UInt64 UnpackSize; UInt64 PackSize; - UInt32 NumIterations; + UInt64 NumIterations; CBenchInfo(): NumIterations(0) {} UInt64 GetUsage() const; UInt64 GetRatingPerUsage(UInt64 rating) const; + UInt64 GetSpeed(UInt64 numCommands) const; }; struct IBenchCallback { + virtual HRESULT SetFreq(bool showFreq, UInt64 cpuFreq) = 0; virtual HRESULT SetEncodeResult(const CBenchInfo &info, bool final) = 0; virtual HRESULT SetDecodeResult(const CBenchInfo &info, bool final) = 0; }; UInt64 GetCompressRating(UInt32 dictSize, UInt64 elapsedTime, UInt64 freq, UInt64 size); -UInt64 GetDecompressRating(UInt64 elapsedTime, UInt64 freq, UInt64 outSize, UInt64 inSize, UInt32 numIterations); +UInt64 GetDecompressRating(UInt64 elapsedTime, UInt64 freq, UInt64 outSize, UInt64 inSize, UInt64 numIterations); -const int kBenchMinDicLogSize = 18; +const unsigned kBenchMinDicLogSize = 18; -UInt64 GetBenchMemoryUsage(UInt32 numThreads, UInt32 dictionary); +UInt64 GetBenchMemoryUsage(UInt32 numThreads, UInt32 dictionary, bool totalBench = false); struct IBenchPrintCallback { @@ -45,7 +47,7 @@ HRESULT Bench( DECL_EXTERNAL_CODECS_LOC_VARS IBenchPrintCallback *printCallback, IBenchCallback *benchCallback, - const CObjectVector<CProperty> props, + const CObjectVector<CProperty> &props, UInt32 numIterations, bool multiDict ); diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/DefaultName.cpp b/3rdparty/lzma/CPP/7zip/UI/Common/DefaultName.cpp index 4335e273154..02f611c3d18 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/DefaultName.cpp +++ b/3rdparty/lzma/CPP/7zip/UI/Common/DefaultName.cpp @@ -7,21 +7,23 @@ static UString GetDefaultName3(const UString &fileName, const UString &extension, const UString &addSubExtension) { - int extLength = extension.Length(); - int fileNameLength = fileName.Length(); - if (fileNameLength > extLength + 1) + const unsigned extLen = extension.Len(); + const unsigned fileNameLen = fileName.Len(); + + if (fileNameLen > extLen + 1) { - int dotPos = fileNameLength - (extLength + 1); + const unsigned dotPos = fileNameLen - (extLen + 1); if (fileName[dotPos] == '.') - if (extension.CompareNoCase(fileName.Mid(dotPos + 1)) == 0) + if (extension.IsEqualTo_NoCase(fileName.Ptr(dotPos + 1))) return fileName.Left(dotPos) + addSubExtension; } - int dotPos = fileName.ReverseFind(L'.'); + + int dotPos = fileName.ReverseFind_Dot(); if (dotPos > 0) return fileName.Left(dotPos) + addSubExtension; if (addSubExtension.IsEmpty()) - return fileName + L"~"; + return fileName + L'~'; else return fileName + addSubExtension; } diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/DefaultName.h b/3rdparty/lzma/CPP/7zip/UI/Common/DefaultName.h index 9764ff8716b..df164560275 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/DefaultName.h +++ b/3rdparty/lzma/CPP/7zip/UI/Common/DefaultName.h @@ -1,9 +1,9 @@ // DefaultName.h -#ifndef __DEFAULTNAME_H -#define __DEFAULTNAME_H +#ifndef __DEFAULT_NAME_H +#define __DEFAULT_NAME_H -#include "Common/MyString.h" +#include "../../../Common/MyString.h" UString GetDefaultName2(const UString &fileName, const UString &extension, const UString &addSubExtension); diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/DirItem.h b/3rdparty/lzma/CPP/7zip/UI/Common/DirItem.h index 6d6dc673465..4286b4e8db0 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/DirItem.h +++ b/3rdparty/lzma/CPP/7zip/UI/Common/DirItem.h @@ -3,10 +3,46 @@ #ifndef __DIR_ITEM_H #define __DIR_ITEM_H -#include "Common/MyString.h" -#include "Common/Types.h" +#include "../../../Common/MyString.h" + +#include "../../../Windows/FileFind.h" + +#include "../../Common/UniqBlocks.h" + #include "../../Archive/IArchive.h" +struct CDirItemsStat +{ + UInt64 NumDirs; + UInt64 NumFiles; + UInt64 NumAltStreams; + UInt64 FilesSize; + UInt64 AltStreamsSize; + + UInt64 NumErrors; + // UInt64 GetTotalItems() const { return NumDirs + NumFiles + NumAltStreams; } + + UInt64 GetTotalBytes() const { return FilesSize + AltStreamsSize; } + + CDirItemsStat(): + NumDirs(0), + NumFiles(0), + NumAltStreams(0), + FilesSize(0), + AltStreamsSize(0), + NumErrors(0) + {} +}; + +#define INTERFACE_IDirItemsCallback(x) \ + virtual HRESULT ScanError(const FString &path, DWORD systemError) x; \ + virtual HRESULT ScanProgress(const CDirItemsStat &st, const FString &path, bool isDir) x; \ + +struct IDirItemsCallback +{ + INTERFACE_IDirItemsCallback(=0) +}; + struct CDirItem { UInt64 Size; @@ -14,11 +50,23 @@ struct CDirItem FILETIME ATime; FILETIME MTime; UString Name; + + #if defined(_WIN32) && !defined(UNDER_CE) + // UString ShortName; + CByteBuffer ReparseData; + CByteBuffer ReparseData2; // fixed (reduced) absolute links + + bool AreReparseData() const { return ReparseData.Size() != 0 || ReparseData2.Size() != 0; } + #endif + UInt32 Attrib; int PhyParent; int LogParent; + int SecureIndex; + + bool IsAltStream; - CDirItem(): PhyParent(-1), LogParent(-1) {} + CDirItem(): PhyParent(-1), LogParent(-1), SecureIndex(-1), IsAltStream(false) {} bool IsDir() const { return (Attrib & FILE_ATTRIBUTE_DIRECTORY) != 0 ; } }; @@ -29,24 +77,63 @@ class CDirItems CIntVector LogParents; UString GetPrefixesPath(const CIntVector &parents, int index, const UString &name) const; + + HRESULT EnumerateDir(int phyParent, int logParent, const FString &phyPrefix); + public: CObjectVector<CDirItem> Items; - int GetNumFolders() const { return Prefixes.Size(); } - UString GetPhyPath(int index) const; - UString GetLogPath(int index) const; + bool SymLinks; - int AddPrefix(int phyParent, int logParent, const UString &prefix); - void DeleteLastPrefix(); + bool ScanAltStreams; + + CDirItemsStat Stat; + + #ifndef UNDER_CE + HRESULT SetLinkInfo(CDirItem &dirItem, const NWindows::NFile::NFind::CFileInfo &fi, + const FString &phyPrefix); + #endif + + + #if defined(_WIN32) && !defined(UNDER_CE) + + CUniqBlocks SecureBlocks; + CByteBuffer TempSecureBuf; + bool _saclEnabled; + bool ReadSecure; + + HRESULT AddSecurityItem(const FString &path, int &secureIndex); + + #endif - void EnumerateDirectory(int phyParent, int logParent, const FString &phyPrefix, - FStringVector &errorPaths, CRecordVector<DWORD> &errorCodes); + IDirItemsCallback *Callback; - void EnumerateDirItems2( + CDirItems(); + + void AddDirFileInfo(int phyParent, int logParent, int secureIndex, + const NWindows::NFile::NFind::CFileInfo &fi); + + HRESULT AddError(const FString &path, DWORD errorCode); + HRESULT AddError(const FString &path); + + HRESULT ScanProgress(const FString &path); + + // unsigned GetNumFolders() const { return Prefixes.Size(); } + FString GetPhyPath(unsigned index) const; + UString GetLogPath(unsigned index) const; + + unsigned AddPrefix(int phyParent, int logParent, const UString &prefix); + void DeleteLastPrefix(); + + HRESULT EnumerateItems2( const FString &phyPrefix, const UString &logPrefix, const FStringVector &filePaths, - FStringVector &errorPaths, CRecordVector<DWORD> &errorCodes); + FStringVector *requestedPaths); + + #if defined(_WIN32) && !defined(UNDER_CE) + void FillFixedReparse(); + #endif void ReserveDown(); }; @@ -57,13 +144,14 @@ struct CArcItem FILETIME MTime; UString Name; bool IsDir; + bool IsAltStream; bool SizeDefined; bool MTimeDefined; bool Censored; UInt32 IndexInServer; int TimeType; - CArcItem(): IsDir(false), SizeDefined(false), MTimeDefined(false), Censored(false), TimeType(-1) {} + CArcItem(): IsDir(false), IsAltStream(false), SizeDefined(false), MTimeDefined(false), Censored(false), TimeType(-1) {} }; #endif diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/EnumDirItems.cpp b/3rdparty/lzma/CPP/7zip/UI/Common/EnumDirItems.cpp index 8c83a9fbdc0..3ebf25aad8f 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/EnumDirItems.cpp +++ b/3rdparty/lzma/CPP/7zip/UI/Common/EnumDirItems.cpp @@ -2,7 +2,18 @@ #include "StdAfx.h" -#include "Windows/FileName.h" +#include <wchar.h> + +#include "../../../Common/Wildcard.h" + +#include "../../../Windows/FileDir.h" +#include "../../../Windows/FileIO.h" +#include "../../../Windows/FileName.h" + +#if defined(_WIN32) && !defined(UNDER_CE) +#define _USE_SECURITY_CODE +#include "../../../Windows/SecurityUtils.h" +#endif #include "EnumDirItems.h" @@ -10,8 +21,8 @@ using namespace NWindows; using namespace NFile; using namespace NName; -void AddDirFileInfo(int phyParent, int logParent, - const NFind::CFileInfo &fi, CObjectVector<CDirItem> &dirItems) +void CDirItems::AddDirFileInfo(int phyParent, int logParent, int secureIndex, + const NFind::CFileInfo &fi) { CDirItem di; di.Size = fi.Size; @@ -19,41 +30,83 @@ void AddDirFileInfo(int phyParent, int logParent, di.ATime = fi.ATime; di.MTime = fi.MTime; di.Attrib = fi.Attrib; + di.IsAltStream = fi.IsAltStream; di.PhyParent = phyParent; di.LogParent = logParent; + di.SecureIndex = secureIndex; di.Name = fs2us(fi.Name); - dirItems.Add(di); + #if defined(_WIN32) && !defined(UNDER_CE) + // di.ShortName = fs2us(fi.ShortName); + #endif + Items.Add(di); + + if (fi.IsDir()) + Stat.NumDirs++; + else if (fi.IsAltStream) + { + Stat.NumAltStreams++; + Stat.AltStreamsSize += fi.Size; + } + else + { + Stat.NumFiles++; + Stat.FilesSize += fi.Size; + } +} + +HRESULT CDirItems::AddError(const FString &path, DWORD errorCode) +{ + Stat.NumErrors++; + if (Callback) + return Callback->ScanError(path, errorCode); + return S_OK; +} + +HRESULT CDirItems::AddError(const FString &path) +{ + return AddError(path, ::GetLastError()); +} + +static const unsigned kScanProgressStepMask = (1 << 12) - 1; + +HRESULT CDirItems::ScanProgress(const FString &dirPath) +{ + if (Callback) + return Callback->ScanProgress(Stat, dirPath, true); + return S_OK; } UString CDirItems::GetPrefixesPath(const CIntVector &parents, int index, const UString &name) const { UString path; - int len = name.Length(); + unsigned len = name.Len(); + int i; for (i = index; i >= 0; i = parents[i]) - len += Prefixes[i].Length(); - int totalLen = len; - wchar_t *p = path.GetBuffer(len); - p[len] = 0; - len -= name.Length(); - memcpy(p + len, (const wchar_t *)name, name.Length() * sizeof(wchar_t)); + len += Prefixes[i].Len(); + + wchar_t *p = path.GetBuf_SetEnd(len) + len; + + p -= name.Len(); + wmemcpy(p, (const wchar_t *)name, name.Len()); + for (i = index; i >= 0; i = parents[i]) { const UString &s = Prefixes[i]; - len -= s.Length(); - memcpy(p + len, (const wchar_t *)s, s.Length() * sizeof(wchar_t)); + p -= s.Len(); + wmemcpy(p, (const wchar_t *)s, s.Len()); } - path.ReleaseBuffer(totalLen); + return path; } -UString CDirItems::GetPhyPath(int index) const +FString CDirItems::GetPhyPath(unsigned index) const { const CDirItem &di = Items[index]; - return GetPrefixesPath(PhyParents, di.PhyParent, di.Name); + return us2fs(GetPrefixesPath(PhyParents, di.PhyParent, di.Name)); } -UString CDirItems::GetLogPath(int index) const +UString CDirItems::GetLogPath(unsigned index) const { const CDirItem &di = Items[index]; return GetPrefixesPath(LogParents, di.LogParent, di.Name); @@ -67,7 +120,7 @@ void CDirItems::ReserveDown() Items.ReserveDown(); } -int CDirItems::AddPrefix(int phyParent, int logParent, const UString &prefix) +unsigned CDirItems::AddPrefix(int phyParent, int logParent, const UString &prefix) { PhyParents.Add(phyParent); LogParents.Add(logParent); @@ -81,150 +134,473 @@ void CDirItems::DeleteLastPrefix() Prefixes.DeleteBack(); } -void CDirItems::EnumerateDirectory(int phyParent, int logParent, const FString &phyPrefix, - FStringVector &errorPaths, CRecordVector<DWORD> &errorCodes) +bool InitLocalPrivileges(); + +CDirItems::CDirItems(): + SymLinks(false), + ScanAltStreams(false) + #ifdef _USE_SECURITY_CODE + , ReadSecure(false) + #endif + , Callback(NULL) { + #ifdef _USE_SECURITY_CODE + _saclEnabled = InitLocalPrivileges(); + #endif +} + +#ifdef _USE_SECURITY_CODE + +HRESULT CDirItems::AddSecurityItem(const FString &path, int &secureIndex) +{ + secureIndex = -1; + + SECURITY_INFORMATION securInfo = + DACL_SECURITY_INFORMATION | + GROUP_SECURITY_INFORMATION | + OWNER_SECURITY_INFORMATION; + if (_saclEnabled) + securInfo |= SACL_SECURITY_INFORMATION; + + DWORD errorCode = 0; + DWORD secureSize; + + BOOL res = ::GetFileSecurityW(fs2us(path), securInfo, (PSECURITY_DESCRIPTOR)(Byte *)TempSecureBuf, (DWORD)TempSecureBuf.Size(), &secureSize); + + if (res) + { + if (secureSize == 0) + return S_OK; + if (secureSize > TempSecureBuf.Size()) + errorCode = ERROR_INVALID_FUNCTION; + } + else + { + errorCode = GetLastError(); + if (errorCode == ERROR_INSUFFICIENT_BUFFER) + { + if (secureSize <= TempSecureBuf.Size()) + errorCode = ERROR_INVALID_FUNCTION; + else + { + TempSecureBuf.Alloc(secureSize); + res = ::GetFileSecurityW(fs2us(path), securInfo, (PSECURITY_DESCRIPTOR)(Byte *)TempSecureBuf, (DWORD)TempSecureBuf.Size(), &secureSize); + if (res) + { + if (secureSize != TempSecureBuf.Size()) + errorCode = ERROR_INVALID_FUNCTION;; + } + else + errorCode = GetLastError(); + } + } + } + + if (res) + { + secureIndex = SecureBlocks.AddUniq(TempSecureBuf, secureSize); + return S_OK; + } + + if (errorCode == 0) + errorCode = ERROR_INVALID_FUNCTION; + return AddError(path, errorCode); +} + +#endif + +HRESULT CDirItems::EnumerateDir(int phyParent, int logParent, const FString &phyPrefix) +{ + RINOK(ScanProgress(phyPrefix)); + NFind::CEnumerator enumerator(phyPrefix + FCHAR_ANY_MASK); - for (;;) + for (unsigned ttt = 0; ; ttt++) { NFind::CFileInfo fi; bool found; if (!enumerator.Next(fi, found)) { - errorCodes.Add(::GetLastError()); - errorPaths.Add(phyPrefix); - return; + return AddError(phyPrefix); } if (!found) - break; - AddDirFileInfo(phyParent, logParent, fi, Items); + return S_OK; + + int secureIndex = -1; + #ifdef _USE_SECURITY_CODE + if (ReadSecure) + { + RINOK(AddSecurityItem(phyPrefix + fi.Name, secureIndex)); + } + #endif + + AddDirFileInfo(phyParent, logParent, secureIndex, fi); + + if (Callback && (ttt & kScanProgressStepMask) == kScanProgressStepMask) + { + RINOK(ScanProgress(phyPrefix)); + } + if (fi.IsDir()) { const FString name2 = fi.Name + FCHAR_PATH_SEPARATOR; - int parent = AddPrefix(phyParent, logParent, fs2us(name2)); - EnumerateDirectory(parent, parent, phyPrefix + name2, errorPaths, errorCodes); + unsigned parent = AddPrefix(phyParent, logParent, fs2us(name2)); + RINOK(EnumerateDir(parent, parent, phyPrefix + name2)); } } } -void CDirItems::EnumerateDirItems2(const FString &phyPrefix, const UString &logPrefix, - const FStringVector &filePaths, FStringVector &errorPaths, CRecordVector<DWORD> &errorCodes) +HRESULT CDirItems::EnumerateItems2( + const FString &phyPrefix, + const UString &logPrefix, + const FStringVector &filePaths, + FStringVector *requestedPaths) { int phyParent = phyPrefix.IsEmpty() ? -1 : AddPrefix(-1, -1, fs2us(phyPrefix)); int logParent = logPrefix.IsEmpty() ? -1 : AddPrefix(-1, -1, logPrefix); - for (int i = 0; i < filePaths.Size(); i++) + FOR_VECTOR (i, filePaths) { const FString &filePath = filePaths[i]; NFind::CFileInfo fi; const FString phyPath = phyPrefix + filePath; if (!fi.Find(phyPath)) { - errorCodes.Add(::GetLastError()); - errorPaths.Add(phyPath); + RINOK(AddError(phyPath)); continue; } - int delimiter = filePath.ReverseFind(FCHAR_PATH_SEPARATOR); + if (requestedPaths) + requestedPaths->Add(phyPath); + + int delimiter = filePath.ReverseFind_PathSepar(); FString phyPrefixCur; int phyParentCur = phyParent; if (delimiter >= 0) { - phyPrefixCur = filePath.Left(delimiter + 1); + phyPrefixCur.SetFrom(filePath, delimiter + 1); phyParentCur = AddPrefix(phyParent, logParent, fs2us(phyPrefixCur)); } - AddDirFileInfo(phyParentCur, logParent, fi, Items); + + int secureIndex = -1; + #ifdef _USE_SECURITY_CODE + if (ReadSecure) + { + RINOK(AddSecurityItem(phyPath, secureIndex)); + } + #endif + + AddDirFileInfo(phyParentCur, logParent, secureIndex, fi); + if (fi.IsDir()) { const FString name2 = fi.Name + FCHAR_PATH_SEPARATOR; - int parent = AddPrefix(phyParentCur, logParent, fs2us(name2)); - EnumerateDirectory(parent, parent, phyPrefix + phyPrefixCur + name2, errorPaths, errorCodes); + unsigned parent = AddPrefix(phyParentCur, logParent, fs2us(name2)); + RINOK(EnumerateDir(parent, parent, phyPrefix + phyPrefixCur + name2)); } } + ReserveDown(); + return S_OK; } -static HRESULT EnumerateDirItems(const NWildcard::CCensorNode &curNode, + + + + + +static HRESULT EnumerateDirItems( + const NWildcard::CCensorNode &curNode, int phyParent, int logParent, const FString &phyPrefix, const UStringVector &addArchivePrefix, CDirItems &dirItems, - bool enterToSubFolders, - IEnumDirItemCallback *callback, - FStringVector &errorPaths, - CRecordVector<DWORD> &errorCodes); + bool enterToSubFolders); -static HRESULT EnumerateDirItems_Spec(const NWildcard::CCensorNode &curNode, +static HRESULT EnumerateDirItems_Spec( + const NWildcard::CCensorNode &curNode, int phyParent, int logParent, const FString &curFolderName, const FString &phyPrefix, const UStringVector &addArchivePrefix, CDirItems &dirItems, - bool enterToSubFolders, - IEnumDirItemCallback *callback, - FStringVector &errorPaths, - CRecordVector<DWORD> &errorCodes) - + bool enterToSubFolders) { const FString name2 = curFolderName + FCHAR_PATH_SEPARATOR; - int parent = dirItems.AddPrefix(phyParent, logParent, fs2us(name2)); - int numItems = dirItems.Items.Size(); - HRESULT res = EnumerateDirItems(curNode, parent, parent, phyPrefix + name2, - addArchivePrefix, dirItems, enterToSubFolders, callback, errorPaths, errorCodes); + unsigned parent = dirItems.AddPrefix(phyParent, logParent, fs2us(name2)); + unsigned numItems = dirItems.Items.Size(); + HRESULT res = EnumerateDirItems( + curNode, parent, parent, phyPrefix + name2, + addArchivePrefix, dirItems, enterToSubFolders); if (numItems == dirItems.Items.Size()) dirItems.DeleteLastPrefix(); return res; } +#ifndef UNDER_CE + +#ifdef _WIN32 + +static HRESULT EnumerateAltStreams( + const NFind::CFileInfo &fi, + const NWildcard::CCensorNode &curNode, + int phyParent, int logParent, const FString &fullPath, + const UStringVector &addArchivePrefix, // prefix from curNode + CDirItems &dirItems) +{ + NFind::CStreamEnumerator enumerator(fullPath); + for (;;) + { + NFind::CStreamInfo si; + bool found; + if (!enumerator.Next(si, found)) + { + return dirItems.AddError(fullPath + FTEXT(":*")); // , (DWORD)E_FAIL + } + if (!found) + return S_OK; + if (si.IsMainStream()) + continue; + UStringVector addArchivePrefixNew = addArchivePrefix; + UString reducedName = si.GetReducedName(); + addArchivePrefixNew.Back() += reducedName; + if (curNode.CheckPathToRoot(false, addArchivePrefixNew, true)) + continue; + NFind::CFileInfo fi2 = fi; + fi2.Name += us2fs(reducedName); + fi2.Size = si.Size; + fi2.Attrib &= ~FILE_ATTRIBUTE_DIRECTORY; + fi2.IsAltStream = true; + dirItems.AddDirFileInfo(phyParent, logParent, -1, fi2); + } +} + +#endif + +HRESULT CDirItems::SetLinkInfo(CDirItem &dirItem, const NFind::CFileInfo &fi, + const FString &phyPrefix) +{ + if (!SymLinks || !fi.HasReparsePoint()) + return S_OK; + const FString path = phyPrefix + fi.Name; + CByteBuffer &buf = dirItem.ReparseData; + if (NIO::GetReparseData(path, buf)) + { + CReparseAttr attr; + if (attr.Parse(buf, buf.Size())) + return S_OK; + } + DWORD res = ::GetLastError(); + buf.Free(); + return AddError(path , res); +} + +#endif -static HRESULT EnumerateDirItems(const NWildcard::CCensorNode &curNode, +static HRESULT EnumerateForItem( + NFind::CFileInfo &fi, + const NWildcard::CCensorNode &curNode, int phyParent, int logParent, const FString &phyPrefix, const UStringVector &addArchivePrefix, // prefix from curNode CDirItems &dirItems, - bool enterToSubFolders, - IEnumDirItemCallback *callback, - FStringVector &errorPaths, - CRecordVector<DWORD> &errorCodes) + bool enterToSubFolders) +{ + const UString name = fs2us(fi.Name); + bool enterToSubFolders2 = enterToSubFolders; + UStringVector addArchivePrefixNew = addArchivePrefix; + addArchivePrefixNew.Add(name); + { + UStringVector addArchivePrefixNewTemp(addArchivePrefixNew); + if (curNode.CheckPathToRoot(false, addArchivePrefixNewTemp, !fi.IsDir())) + return S_OK; + } + int dirItemIndex = -1; + + if (curNode.CheckPathToRoot(true, addArchivePrefixNew, !fi.IsDir())) + { + int secureIndex = -1; + #ifdef _USE_SECURITY_CODE + if (dirItems.ReadSecure) + { + RINOK(dirItems.AddSecurityItem(phyPrefix + fi.Name, secureIndex)); + } + #endif + + dirItemIndex = dirItems.Items.Size(); + dirItems.AddDirFileInfo(phyParent, logParent, secureIndex, fi); + if (fi.IsDir()) + enterToSubFolders2 = true; + } + + #ifndef UNDER_CE + if (dirItems.ScanAltStreams) + { + RINOK(EnumerateAltStreams(fi, curNode, phyParent, logParent, + phyPrefix + fi.Name, + addArchivePrefixNew, dirItems)); + } + + if (dirItemIndex >= 0) + { + CDirItem &dirItem = dirItems.Items[dirItemIndex]; + RINOK(dirItems.SetLinkInfo(dirItem, fi, phyPrefix)); + if (dirItem.ReparseData.Size() != 0) + return S_OK; + } + #endif + + if (!fi.IsDir()) + return S_OK; + + const NWildcard::CCensorNode *nextNode = 0; + if (addArchivePrefix.IsEmpty()) + { + int index = curNode.FindSubNode(name); + if (index >= 0) + nextNode = &curNode.SubNodes[index]; + } + if (!enterToSubFolders2 && nextNode == 0) + return S_OK; + + addArchivePrefixNew = addArchivePrefix; + if (nextNode == 0) + { + nextNode = &curNode; + addArchivePrefixNew.Add(name); + } + + return EnumerateDirItems_Spec( + *nextNode, phyParent, logParent, fi.Name, phyPrefix, + addArchivePrefixNew, + dirItems, + enterToSubFolders2); +} + + +static bool CanUseFsDirect(const NWildcard::CCensorNode &curNode) +{ + FOR_VECTOR (i, curNode.IncludeItems) + { + const NWildcard::CItem &item = curNode.IncludeItems[i]; + if (item.Recursive || item.PathParts.Size() != 1) + return false; + const UString &name = item.PathParts.Front(); + /* + if (name.IsEmpty()) + return false; + */ + + /* Windows doesn't support file name with wildcard + But if another system supports file name with wildcard, + and wildcard mode is disabled, we can ignore wildcard in name */ + /* + if (!item.WildcardParsing) + continue; + */ + if (DoesNameContainWildcard(name)) + return false; + } + return true; +} + + +#if defined(_WIN32) && !defined(UNDER_CE) + +static bool IsVirtualFsFolder(const FString &prefix, const UString &name) +{ + UString s = fs2us(prefix); + s += name; + s.Add_PathSepar(); + return IsPathSepar(s[0]) && GetRootPrefixSize(s) == 0; +} + +#endif + +static HRESULT EnumerateDirItems( + const NWildcard::CCensorNode &curNode, + int phyParent, int logParent, const FString &phyPrefix, + const UStringVector &addArchivePrefix, // prefix from curNode + CDirItems &dirItems, + bool enterToSubFolders) { if (!enterToSubFolders) if (curNode.NeedCheckSubDirs()) enterToSubFolders = true; - if (callback) - RINOK(callback->ScanProgress(dirItems.GetNumFolders(), dirItems.Items.Size(), fs2us(phyPrefix))); + + RINOK(dirItems.ScanProgress(phyPrefix)); // try direct_names case at first if (addArchivePrefix.IsEmpty() && !enterToSubFolders) { - // check that all names are direct - int i; - for (i = 0; i < curNode.IncludeItems.Size(); i++) - { - const NWildcard::CItem &item = curNode.IncludeItems[i]; - if (item.Recursive || item.PathParts.Size() != 1) - break; - const UString &name = item.PathParts.Front(); - if (name.IsEmpty() || DoesNameContainWildCard(name)) - break; - } - if (i == curNode.IncludeItems.Size()) + if (CanUseFsDirect(curNode)) { // all names are direct (no wildcards) // so we don't need file_system's dir enumerator CRecordVector<bool> needEnterVector; + unsigned i; + for (i = 0; i < curNode.IncludeItems.Size(); i++) { const NWildcard::CItem &item = curNode.IncludeItems[i]; const UString &name = item.PathParts.Front(); - const FString fullPath = phyPrefix + us2fs(name); + FString fullPath = phyPrefix + us2fs(name); + + #if defined(_WIN32) && !defined(UNDER_CE) + bool needAltStreams = true; + #endif + + #ifdef _USE_SECURITY_CODE + bool needSecurity = true; + #endif + + if (phyPrefix.IsEmpty()) + { + if (!item.ForFile) + { + /* we don't like some names for alt streams inside archive: + ":sname" for "\" + "c:::sname" for "C:\" + So we ignore alt streams for these cases */ + if (name.IsEmpty()) + { + #if defined(_WIN32) && !defined(UNDER_CE) + needAltStreams = false; + #endif + + /* + // do we need to ignore security info for "\\" folder ? + #ifdef _USE_SECURITY_CODE + needSecurity = false; + #endif + */ + + fullPath = FCHAR_PATH_SEPARATOR; + } + #if defined(_WIN32) && !defined(UNDER_CE) + else if (item.IsDriveItem()) + { + needAltStreams = false; + fullPath.Add_PathSepar(); + } + #endif + } + } + NFind::CFileInfo fi; + #if defined(_WIN32) && !defined(UNDER_CE) + if (IsVirtualFsFolder(phyPrefix, name)) + { + fi.SetAsDir(); + fi.Name = us2fs(name); + } + else + #endif if (!fi.Find(fullPath)) { - errorCodes.Add(::GetLastError()); - errorPaths.Add(fullPath); + RINOK(dirItems.AddError(fullPath)); continue; } + bool isDir = fi.IsDir(); if (isDir && !item.ForDir || !isDir && !item.ForFile) { - errorCodes.Add((DWORD)E_FAIL); - errorPaths.Add(fullPath); + RINOK(dirItems.AddError(fullPath, (DWORD)E_FAIL)); continue; } { @@ -233,7 +609,43 @@ static HRESULT EnumerateDirItems(const NWildcard::CCensorNode &curNode, if (curNode.CheckPathToRoot(false, pathParts, !isDir)) continue; } - AddDirFileInfo(phyParent, logParent, fi, dirItems.Items); + + int secureIndex = -1; + #ifdef _USE_SECURITY_CODE + if (needSecurity && dirItems.ReadSecure) + { + RINOK(dirItems.AddSecurityItem(fullPath, secureIndex)); + } + #endif + + dirItems.AddDirFileInfo(phyParent, logParent, secureIndex, fi); + + #ifndef UNDER_CE + { + CDirItem &dirItem = dirItems.Items.Back(); + RINOK(dirItems.SetLinkInfo(dirItem, fi, phyPrefix)); + if (dirItem.ReparseData.Size() != 0) + { + if (fi.IsAltStream) + dirItems.Stat.AltStreamsSize -= fi.Size; + else + dirItems.Stat.FilesSize -= fi.Size; + continue; + } + } + #endif + + + #ifndef UNDER_CE + if (needAltStreams && dirItems.ScanAltStreams) + { + UStringVector pathParts; + pathParts.Add(fs2us(fi.Name)); + RINOK(EnumerateAltStreams(fi, curNode, phyParent, logParent, + fullPath, pathParts, dirItems)); + } + #endif + if (!isDir) continue; @@ -254,110 +666,233 @@ static HRESULT EnumerateDirItems(const NWildcard::CCensorNode &curNode, } RINOK(EnumerateDirItems_Spec(*nextNode, phyParent, logParent, fi.Name, phyPrefix, - addArchivePrefixNew, dirItems, true, callback, errorPaths, errorCodes)); + addArchivePrefixNew, dirItems, true)); } + for (i = 0; i < curNode.SubNodes.Size(); i++) { if (i < needEnterVector.Size()) if (!needEnterVector[i]) continue; const NWildcard::CCensorNode &nextNode = curNode.SubNodes[i]; - const FString fullPath = phyPrefix + us2fs(nextNode.Name); + FString fullPath = phyPrefix + us2fs(nextNode.Name); NFind::CFileInfo fi; - if (!fi.Find(fullPath)) + + if (phyPrefix.IsEmpty()) { - if (!nextNode.AreThereIncludeItems()) - continue; - errorCodes.Add(::GetLastError()); - errorPaths.Add(fullPath); - continue; + { + if (nextNode.Name.IsEmpty()) + fullPath = FCHAR_PATH_SEPARATOR; + #ifdef _WIN32 + else if (NWildcard::IsDriveColonName(nextNode.Name)) + fullPath.Add_PathSepar(); + #endif + } } - if (!fi.IsDir()) + + // we don't want to call fi.Find() for root folder or virtual folder + if (phyPrefix.IsEmpty() && nextNode.Name.IsEmpty() + #if defined(_WIN32) && !defined(UNDER_CE) + || IsVirtualFsFolder(phyPrefix, nextNode.Name) + #endif + ) { - errorCodes.Add((DWORD)E_FAIL); - errorPaths.Add(fullPath); - continue; + fi.SetAsDir(); + fi.Name = us2fs(nextNode.Name); + } + else + { + if (!fi.Find(fullPath)) + { + if (!nextNode.AreThereIncludeItems()) + continue; + RINOK(dirItems.AddError(fullPath)); + continue; + } + + if (!fi.IsDir()) + { + RINOK(dirItems.AddError(fullPath, (DWORD)E_FAIL)); + continue; + } } RINOK(EnumerateDirItems_Spec(nextNode, phyParent, logParent, fi.Name, phyPrefix, - UStringVector(), dirItems, false, callback, errorPaths, errorCodes)); + UStringVector(), dirItems, false)); } + return S_OK; } } + #ifdef _WIN32 + #ifndef UNDER_CE + + // scan drives, if wildcard is "*:\" + + if (phyPrefix.IsEmpty() && curNode.IncludeItems.Size() > 0) + { + unsigned i; + for (i = 0; i < curNode.IncludeItems.Size(); i++) + { + const NWildcard::CItem &item = curNode.IncludeItems[i]; + if (item.PathParts.Size() < 1) + break; + const UString &name = item.PathParts.Front(); + if (name.Len() != 2 || name[1] != ':') + break; + if (item.PathParts.Size() == 1) + if (item.ForFile || !item.ForDir) + break; + if (NWildcard::IsDriveColonName(name)) + continue; + if (name[0] != '*' && name[0] != '?') + break; + } + if (i == curNode.IncludeItems.Size()) + { + FStringVector driveStrings; + NFind::MyGetLogicalDriveStrings(driveStrings); + for (i = 0; i < driveStrings.Size(); i++) + { + FString driveName = driveStrings[i]; + if (driveName.Len() < 3 || driveName.Back() != '\\') + return E_FAIL; + driveName.DeleteBack(); + NFind::CFileInfo fi; + fi.SetAsDir(); + fi.Name = driveName; + + RINOK(EnumerateForItem(fi, curNode, phyParent, logParent, phyPrefix, + addArchivePrefix, dirItems, enterToSubFolders)); + } + return S_OK; + } + } + + #endif + #endif NFind::CEnumerator enumerator(phyPrefix + FCHAR_ANY_MASK); - for (int ttt = 0; ; ttt++) + for (unsigned ttt = 0; ; ttt++) { NFind::CFileInfo fi; bool found; if (!enumerator.Next(fi, found)) { - errorCodes.Add(::GetLastError()); - errorPaths.Add(phyPrefix); + RINOK(dirItems.AddError(phyPrefix)); break; } if (!found) break; - if (callback && (ttt & 0xFF) == 0xFF) - RINOK(callback->ScanProgress(dirItems.GetNumFolders(), dirItems.Items.Size(), fs2us(phyPrefix))); - const UString &name = fs2us(fi.Name); - bool enterToSubFolders2 = enterToSubFolders; - UStringVector addArchivePrefixNew = addArchivePrefix; - addArchivePrefixNew.Add(name); - { - UStringVector addArchivePrefixNewTemp(addArchivePrefixNew); - if (curNode.CheckPathToRoot(false, addArchivePrefixNewTemp, !fi.IsDir())) - continue; - } - if (curNode.CheckPathToRoot(true, addArchivePrefixNew, !fi.IsDir())) - { - AddDirFileInfo(phyParent, logParent, fi, dirItems.Items); - if (fi.IsDir()) - enterToSubFolders2 = true; - } - if (!fi.IsDir()) - continue; - - const NWildcard::CCensorNode *nextNode = 0; - if (addArchivePrefix.IsEmpty()) - { - int index = curNode.FindSubNode(name); - if (index >= 0) - nextNode = &curNode.SubNodes[index]; - } - if (!enterToSubFolders2 && nextNode == 0) - continue; - - addArchivePrefixNew = addArchivePrefix; - if (nextNode == 0) + if (dirItems.Callback && (ttt & kScanProgressStepMask) == kScanProgressStepMask) { - nextNode = &curNode; - addArchivePrefixNew.Add(name); + RINOK(dirItems.ScanProgress(phyPrefix)); } - RINOK(EnumerateDirItems_Spec(*nextNode, phyParent, logParent, fi.Name, phyPrefix, - addArchivePrefixNew, dirItems, enterToSubFolders2, callback, errorPaths, errorCodes)); + RINOK(EnumerateForItem(fi, curNode, phyParent, logParent, phyPrefix, + addArchivePrefix, dirItems, enterToSubFolders)); } + return S_OK; } HRESULT EnumerateItems( const NWildcard::CCensor &censor, - CDirItems &dirItems, - IEnumDirItemCallback *callback, - FStringVector &errorPaths, - CRecordVector<DWORD> &errorCodes) + const NWildcard::ECensorPathMode pathMode, + const UString &addPathPrefix, + CDirItems &dirItems) { - for (int i = 0; i < censor.Pairs.Size(); i++) + FOR_VECTOR (i, censor.Pairs) { const NWildcard::CPair &pair = censor.Pairs[i]; int phyParent = pair.Prefix.IsEmpty() ? -1 : dirItems.AddPrefix(-1, -1, pair.Prefix); - RINOK(EnumerateDirItems(pair.Head, phyParent, -1, us2fs(pair.Prefix), UStringVector(), dirItems, false, - callback, errorPaths, errorCodes)); + int logParent = -1; + + if (pathMode == NWildcard::k_AbsPath) + logParent = phyParent; + else + { + if (!addPathPrefix.IsEmpty()) + logParent = dirItems.AddPrefix(-1, -1, addPathPrefix); + } + + RINOK(EnumerateDirItems(pair.Head, phyParent, logParent, us2fs(pair.Prefix), UStringVector(), + dirItems, + false // enterToSubFolders + )); } dirItems.ReserveDown(); + + #if defined(_WIN32) && !defined(UNDER_CE) + dirItems.FillFixedReparse(); + #endif + return S_OK; } + +#if defined(_WIN32) && !defined(UNDER_CE) + +void CDirItems::FillFixedReparse() +{ + /* imagex/WIM reduces absolute pathes in links (raparse data), + if we archive non root folder. We do same thing here */ + + if (!SymLinks) + return; + + FOR_VECTOR(i, Items) + { + CDirItem &item = Items[i]; + if (item.ReparseData.Size() == 0) + continue; + + CReparseAttr attr; + if (!attr.Parse(item.ReparseData, item.ReparseData.Size())) + continue; + if (attr.IsRelative()) + continue; + + const UString &link = attr.GetPath(); + if (!IsDrivePath(link)) + continue; + // maybe we need to support networks paths also ? + + FString fullPathF; + if (!NDir::MyGetFullPathName(GetPhyPath(i), fullPathF)) + continue; + UString fullPath = fs2us(fullPathF); + const UString logPath = GetLogPath(i); + if (logPath.Len() >= fullPath.Len()) + continue; + if (CompareFileNames(logPath, fullPath.RightPtr(logPath.Len())) != 0) + continue; + + const UString prefix = fullPath.Left(fullPath.Len() - logPath.Len()); + if (!IsPathSepar(prefix.Back())) + continue; + + unsigned rootPrefixSize = GetRootPrefixSize(prefix); + if (rootPrefixSize == 0) + continue; + if (rootPrefixSize == prefix.Len()) + continue; // simple case: paths are from root + + if (link.Len() <= prefix.Len()) + continue; + + if (CompareFileNames(link.Left(prefix.Len()), prefix) != 0) + continue; + + UString newLink = prefix.Left(rootPrefixSize); + newLink += link.Ptr(prefix.Len()); + + CByteBuffer data; + if (!FillLinkData(data, newLink, attr.IsSymLink())) + continue; + item.ReparseData2 = data; + } +} + +#endif diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/EnumDirItems.h b/3rdparty/lzma/CPP/7zip/UI/Common/EnumDirItems.h index c96c7b9e83e..7afb800a033 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/EnumDirItems.h +++ b/3rdparty/lzma/CPP/7zip/UI/Common/EnumDirItems.h @@ -3,23 +3,19 @@ #ifndef __ENUM_DIR_ITEMS_H #define __ENUM_DIR_ITEMS_H -#include "Common/Wildcard.h" -#include "Windows/FileFind.h" +#include "../../../Common/Wildcard.h" + +#include "../../../Windows/FileFind.h" + #include "DirItem.h" -void AddDirFileInfo(int phyParent, int logParent, +void AddDirFileInfo(int phyParent, int logParent, int secureIndex, const NWindows::NFile::NFind::CFileInfo &fi, CObjectVector<CDirItem> &dirItems); -struct IEnumDirItemCallback -{ - virtual HRESULT ScanProgress(UInt64 numFolders, UInt64 numFiles, const wchar_t *path) = 0; -}; - HRESULT EnumerateItems( const NWildcard::CCensor &censor, - CDirItems &dirItems, - IEnumDirItemCallback *callback, - FStringVector &errorPaths, - CRecordVector<DWORD> &errorCodes); + NWildcard::ECensorPathMode pathMode, + const UString &addPathPrefix, + CDirItems &dirItems); #endif diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/Extract.cpp b/3rdparty/lzma/CPP/7zip/UI/Common/Extract.cpp index 81186772a62..14aa74072bc 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/Extract.cpp +++ b/3rdparty/lzma/CPP/7zip/UI/Common/Extract.cpp @@ -2,11 +2,13 @@ #include "StdAfx.h" -#include <stdio.h> +#include "../../../../C/Sort.h" -#include "Windows/FileDir.h" -#include "Windows/PropVariant.h" -#include "Windows/PropVariantConversions.h" +#include "../../../Common/StringConvert.h" + +#include "../../../Windows/FileDir.h" +#include "../../../Windows/PropVariant.h" +#include "../../../Windows/PropVariantConv.h" #include "../Common/ExtractingFilePath.h" @@ -14,111 +16,249 @@ #include "SetProperties.h" using namespace NWindows; +using namespace NFile; +using namespace NDir; static HRESULT DecompressArchive( - const CArc &arc, + CCodecs *codecs, + const CArchiveLink &arcLink, UInt64 packSize, const NWildcard::CCensorNode &wildcardCensor, const CExtractOptions &options, + bool calcCrc, IExtractCallbackUI *callback, - CArchiveExtractCallback *extractCallbackSpec, + CArchiveExtractCallback *ecs, UString &errorMessage, UInt64 &stdInProcessed) { + const CArc &arc = arcLink.Arcs.Back(); stdInProcessed = 0; IInArchive *archive = arc.Archive; CRecordVector<UInt32> realIndices; + + UStringVector removePathParts; + + FString outDir = options.OutputDir; + UString replaceName = arc.DefaultName; + + if (arcLink.Arcs.Size() > 1) + { + // Most "pe" archives have same name of archive subfile "[0]" or ".rsrc_1". + // So it extracts different archives to one folder. + // We will use top level archive name + const CArc &arc0 = arcLink.Arcs[0]; + if (StringsAreEqualNoCase_Ascii(codecs->Formats[arc0.FormatIndex].Name, "pe")) + replaceName = arc0.DefaultName; + } + + outDir.Replace(FSTRING_ANY_MASK, us2fs(Get_Correct_FsFile_Name(replaceName))); + + bool elimIsPossible = false; + UString elimPrefix; // only pure name without dir delimiter + FString outDirReduced = outDir; + + if (options.ElimDup.Val && options.PathMode != NExtract::NPathMode::kAbsPaths) + { + UString dirPrefix; + SplitPathToParts_Smart(fs2us(outDir), dirPrefix, elimPrefix); + if (!elimPrefix.IsEmpty()) + { + if (IsPathSepar(elimPrefix.Back())) + elimPrefix.DeleteBack(); + if (!elimPrefix.IsEmpty()) + { + outDirReduced = us2fs(dirPrefix); + elimIsPossible = true; + } + } + } + + bool allFilesAreAllowed = wildcardCensor.AreAllAllowed(); + if (!options.StdInMode) { UInt32 numItems; RINOK(archive->GetNumberOfItems(&numItems)); + CReadArcItem item; + for (UInt32 i = 0; i < numItems; i++) { - UString filePath; - RINOK(arc.GetItemPath(i, filePath)); - bool isFolder; - RINOK(IsArchiveItemFolder(archive, i, isFolder)); - if (!wildcardCensor.CheckPath(filePath, !isFolder)) + if (elimIsPossible || !allFilesAreAllowed) + { + RINOK(arc.GetItem(i, item)); + } + else + { + #ifdef SUPPORT_ALT_STREAMS + item.IsAltStream = false; + if (!options.NtOptions.AltStreams.Val && arc.Ask_AltStream) + { + RINOK(Archive_IsItem_AltStream(arc.Archive, i, item.IsAltStream)); + } + #endif + } + + #ifdef SUPPORT_ALT_STREAMS + if (!options.NtOptions.AltStreams.Val && item.IsAltStream) continue; + #endif + + if (elimIsPossible) + { + const UString &s = + #ifdef SUPPORT_ALT_STREAMS + item.MainPath; + #else + item.Path; + #endif + if (!IsPath1PrefixedByPath2(s, elimPrefix)) + elimIsPossible = false; + else + { + wchar_t c = s[elimPrefix.Len()]; + if (c == 0) + { + if (!item.MainIsDir) + elimIsPossible = false; + } + else if (!IsPathSepar(c)) + elimIsPossible = false; + } + } + + if (!allFilesAreAllowed) + { + if (!CensorNode_CheckPath(wildcardCensor, item)) + continue; + } + realIndices.Add(i); } + if (realIndices.Size() == 0) { callback->ThereAreNoFiles(); - return S_OK; + return callback->ExtractResult(S_OK); } } - UStringVector removePathParts; + if (elimIsPossible) + { + removePathParts.Add(elimPrefix); + // outDir = outDirReduced; + } - FString outDir = options.OutputDir; - outDir.Replace(FSTRING_ANY_MASK, us2fs(GetCorrectFsPath(arc.DefaultName))); #ifdef _WIN32 // GetCorrectFullFsPath doesn't like "..". // outDir.TrimRight(); // outDir = GetCorrectFullFsPath(outDir); #endif - if (!outDir.IsEmpty()) - if (!NFile::NDirectory::CreateComplexDirectory(outDir)) - { - HRESULT res = ::GetLastError(); - if (res == S_OK) - res = E_FAIL; - errorMessage = ((UString)L"Can not create output directory ") + fs2us(outDir); - return res; - } + if (outDir.IsEmpty()) + outDir = FTEXT(".") FSTRING_PATH_SEPARATOR; + /* + #ifdef _WIN32 + else if (NName::IsAltPathPrefix(outDir)) {} + #endif + */ + else if (!CreateComplexDir(outDir)) + { + HRESULT res = ::GetLastError(); + if (res == S_OK) + res = E_FAIL; + errorMessage.SetFromAscii("Can not create output directory: "); + errorMessage += fs2us(outDir); + return res; + } - extractCallbackSpec->Init( + ecs->Init( + options.NtOptions, options.StdInMode ? &wildcardCensor : NULL, &arc, callback, - options.StdOutMode, options.TestMode, options.CalcCrc, + options.StdOutMode, options.TestMode, outDir, - removePathParts, + removePathParts, false, packSize); - #if !defined(_7ZIP_ST) && !defined(_SFX) - RINOK(SetProperties(archive, options.Properties)); + + #ifdef SUPPORT_LINKS + + if (!options.StdInMode && + !options.TestMode && + options.NtOptions.HardLinks.Val) + { + RINOK(ecs->PrepareHardLinks(&realIndices)); + } + #endif + HRESULT result; - Int32 testMode = (options.TestMode && !options.CalcCrc) ? 1: 0; + Int32 testMode = (options.TestMode && !calcCrc) ? 1: 0; if (options.StdInMode) { - result = archive->Extract(NULL, (UInt32)(Int32)-1, testMode, extractCallbackSpec); + result = archive->Extract(NULL, (UInt32)(Int32)-1, testMode, ecs); NCOM::CPropVariant prop; if (archive->GetArchiveProperty(kpidPhySize, &prop) == S_OK) - if (prop.vt == VT_UI8 || prop.vt == VT_UI4) - stdInProcessed = ConvertPropVariantToUInt64(prop); + ConvertPropVariantToUInt64(prop, stdInProcessed); } else - result = archive->Extract(&realIndices.Front(), realIndices.Size(), testMode, extractCallbackSpec); - + result = archive->Extract(&realIndices.Front(), realIndices.Size(), testMode, ecs); + if (result == S_OK && !options.StdInMode) + result = ecs->SetDirsTimes(); return callback->ExtractResult(result); } -HRESULT DecompressArchives( - CCodecs *codecs, const CIntVector &formatIndices, +/* v9.31: BUG was fixed: + Sorted list for file paths was sorted with case insensitive compare function. + But FindInSorted function did binary search via case sensitive compare function */ + +int Find_FileName_InSortedVector(const UStringVector &fileName, const UString &name) +{ + unsigned left = 0, right = fileName.Size(); + while (left != right) + { + unsigned mid = (left + right) / 2; + const UString &midValue = fileName[mid]; + int compare = CompareFileNames(name, midValue); + if (compare == 0) + return mid; + if (compare < 0) + right = mid; + else + left = mid + 1; + } + return -1; +} + +HRESULT Extract( + CCodecs *codecs, + const CObjectVector<COpenType> &types, + const CIntVector &excludedFormats, UStringVector &arcPaths, UStringVector &arcPathsFull, const NWildcard::CCensorNode &wildcardCensor, const CExtractOptions &options, IOpenCallbackUI *openCallback, IExtractCallbackUI *extractCallback, + #ifndef _SFX + IHashCalc *hash, + #endif UString &errorMessage, - CDecompressStat &stat) + CDecompressStat &st) { - stat.Clear(); - int i; + st.Clear(); UInt64 totalPackSize = 0; - CRecordVector<UInt64> archiveSizes; + CRecordVector<UInt64> arcSizes; - int numArcs = options.StdInMode ? 1 : arcPaths.Size(); + unsigned numArcs = options.StdInMode ? 1 : arcPaths.Size(); + unsigned i; + for (i = 0; i < numArcs; i++) { - NFile::NFind::CFileInfo fi; + NFind::CFileInfo fi; fi.Size = 0; if (!options.StdInMode) { @@ -128,21 +268,37 @@ HRESULT DecompressArchives( if (fi.IsDir()) throw "can't decompress folder"; } - archiveSizes.Add(fi.Size); + arcSizes.Add(fi.Size); totalPackSize += fi.Size; } - CArchiveExtractCallback *extractCallbackSpec = new CArchiveExtractCallback; - CMyComPtr<IArchiveExtractCallback> ec(extractCallbackSpec); + + CBoolArr skipArcs(numArcs); + for (i = 0; i < numArcs; i++) + skipArcs[i] = false; + + CArchiveExtractCallback *ecs = new CArchiveExtractCallback; + CMyComPtr<IArchiveExtractCallback> ec(ecs); bool multi = (numArcs > 1); - extractCallbackSpec->InitForMulti(multi, options.PathMode, options.OverwriteMode); + ecs->InitForMulti(multi, options.PathMode, options.OverwriteMode); + #ifndef _SFX + ecs->SetHashMethods(hash); + #endif + if (multi) { RINOK(extractCallback->SetTotal(totalPackSize)); } + + UInt64 totalPackProcessed = 0; + bool thereAreNotOpenArcs = false; + for (i = 0; i < numArcs; i++) { + if (skipArcs[i]) + continue; + const UString &arcPath = arcPaths[i]; - NFile::NFind::CFileInfo fi; + NFind::CFileInfo fi; if (options.StdInMode) { fi.Size = 0; @@ -154,21 +310,24 @@ HRESULT DecompressArchives( throw "there is no such archive"; } + /* #ifndef _NO_CRYPTO - openCallback->Open_ClearPasswordWasAskedFlag(); + openCallback->Open_Clear_PasswordWasAsked_Flag(); #endif + */ - RINOK(extractCallback->BeforeOpen(arcPath)); - CArchiveLink archiveLink; + RINOK(extractCallback->BeforeOpen(arcPath, options.TestMode)); + CArchiveLink arcLink; - CIntVector formatIndices2 = formatIndices; + CObjectVector<COpenType> types2 = types; + /* #ifndef _SFX - if (formatIndices.IsEmpty()) + if (types.IsEmpty()) { int pos = arcPath.ReverseFind(L'.'); if (pos >= 0) { - UString s = arcPath.Mid(pos + 1); + UString s = arcPath.Ptr(pos + 1); int index = codecs->FindFormatForExtension(s); if (index >= 0 && s == L"001") { @@ -176,88 +335,143 @@ HRESULT DecompressArchives( pos = s.ReverseFind(L'.'); if (pos >= 0) { - int index2 = codecs->FindFormatForExtension(s.Mid(pos + 1)); - if (index2 >= 0 && s.CompareNoCase(L"rar") != 0) + int index2 = codecs->FindFormatForExtension(s.Ptr(pos + 1)); + if (index2 >= 0) // && s.CompareNoCase(L"rar") != 0 { - formatIndices2.Add(index2); - formatIndices2.Add(index); + types2.Add(index2); + types2.Add(index); } } } } } #endif - HRESULT result = archiveLink.Open2(codecs, formatIndices2, options.StdInMode, NULL, arcPath, openCallback); + */ + + COpenOptions op; + #ifndef _SFX + op.props = &options.Properties; + #endif + op.codecs = codecs; + op.types = &types2; + op.excludedFormats = &excludedFormats; + op.stdInMode = options.StdInMode; + op.stream = NULL; + op.filePath = arcPath; + + HRESULT result = arcLink.Open3(op, openCallback); + if (result == E_ABORT) return result; - bool crypted = false; - #ifndef _NO_CRYPTO - crypted = openCallback->Open_WasPasswordAsked(); - #endif + if (result == S_OK && arcLink.NonOpen_ErrorInfo.ErrorFormatIndex >= 0) + result = S_FALSE; + + // arcLink.Set_ErrorsText(); + RINOK(extractCallback->OpenResult(codecs, arcLink, arcPath, result)); - RINOK(extractCallback->OpenResult(arcPath, result, crypted)); if (result != S_OK) + { + thereAreNotOpenArcs = true; + if (!options.StdInMode) + { + NFind::CFileInfo fi; + if (fi.Find(us2fs(arcPath))) + if (!fi.IsDir()) + totalPackProcessed += fi.Size; + } continue; + } if (!options.StdInMode) - for (int v = 0; v < archiveLink.VolumePaths.Size(); v++) { - int index = arcPathsFull.FindInSorted(archiveLink.VolumePaths[v]); - if (index >= 0 && index > i) + // numVolumes += arcLink.VolumePaths.Size(); + // arcLink.VolumesSize; + + // totalPackSize -= DeleteUsedFileNamesFromList(arcLink, i + 1, arcPaths, arcPathsFull, &arcSizes); + // numArcs = arcPaths.Size(); + if (arcLink.VolumePaths.Size() != 0) { - arcPaths.Delete(index); - arcPathsFull.Delete(index); - totalPackSize -= archiveSizes[index]; - archiveSizes.Delete(index); - numArcs = arcPaths.Size(); + Int64 correctionSize = arcLink.VolumesSize; + FOR_VECTOR (v, arcLink.VolumePaths) + { + int index = Find_FileName_InSortedVector(arcPathsFull, arcLink.VolumePaths[v]); + if (index >= 0) + { + if ((unsigned)index > i) + { + skipArcs[(unsigned)index] = true; + correctionSize -= arcSizes[(unsigned)index]; + } + } + } + if (correctionSize != 0) + { + Int64 newPackSize = (Int64)totalPackSize + correctionSize; + if (newPackSize < 0) + newPackSize = 0; + totalPackSize = newPackSize; + RINOK(extractCallback->SetTotal(totalPackSize)); + } } } - if (archiveLink.VolumePaths.Size() != 0) - { - totalPackSize += archiveLink.VolumesSize; - RINOK(extractCallback->SetTotal(totalPackSize)); - } + + /* + // Now openCallback and extractCallback use same object. So we don't need to send password. #ifndef _NO_CRYPTO + bool passwordIsDefined; UString password; - RINOK(openCallback->Open_GetPasswordIfAny(password)); - if (!password.IsEmpty()) + RINOK(openCallback->Open_GetPasswordIfAny(passwordIsDefined, password)); + if (passwordIsDefined) { RINOK(extractCallback->SetPassword(password)); } #endif + */ - for (int v = 0; v < archiveLink.Arcs.Size(); v++) - { - const UString &s = archiveLink.Arcs[v].ErrorMessage; - if (!s.IsEmpty()) - { - RINOK(extractCallback->MessageError(s)); - } - } - - CArc &arc = archiveLink.Arcs.Back(); + CArc &arc = arcLink.Arcs.Back(); arc.MTimeDefined = (!options.StdInMode && !fi.IsDevice); arc.MTime = fi.MTime; UInt64 packProcessed; - RINOK(DecompressArchive(arc, - fi.Size + archiveLink.VolumesSize, - wildcardCensor, options, extractCallback, extractCallbackSpec, errorMessage, packProcessed)); + bool calcCrc = + #ifndef _SFX + (hash != NULL); + #else + false; + #endif + + RINOK(DecompressArchive( + codecs, + arcLink, + fi.Size + arcLink.VolumesSize, + wildcardCensor, + options, + calcCrc, + extractCallback, ecs, errorMessage, packProcessed)); + if (!options.StdInMode) - packProcessed = fi.Size + archiveLink.VolumesSize; - extractCallbackSpec->LocalProgressSpec->InSize += packProcessed; - extractCallbackSpec->LocalProgressSpec->OutSize = extractCallbackSpec->UnpackSize; + packProcessed = fi.Size + arcLink.VolumesSize; + totalPackProcessed += packProcessed; + ecs->LocalProgressSpec->InSize += packProcessed; + ecs->LocalProgressSpec->OutSize = ecs->UnpackSize; if (!errorMessage.IsEmpty()) return E_FAIL; } - stat.NumFolders = extractCallbackSpec->NumFolders; - stat.NumFiles = extractCallbackSpec->NumFiles; - stat.UnpackSize = extractCallbackSpec->UnpackSize; - stat.CrcSum = extractCallbackSpec->CrcSum; - stat.NumArchives = arcPaths.Size(); - stat.PackSize = extractCallbackSpec->LocalProgressSpec->InSize; + if (multi || thereAreNotOpenArcs) + { + RINOK(extractCallback->SetTotal(totalPackSize)); + RINOK(extractCallback->SetCompleted(&totalPackProcessed)); + } + + st.NumFolders = ecs->NumFolders; + st.NumFiles = ecs->NumFiles; + st.NumAltStreams = ecs->NumAltStreams; + st.UnpackSize = ecs->UnpackSize; + st.AltStreams_UnpackSize = ecs->AltStreams_UnpackSize; + st.NumArchives = arcPaths.Size(); + st.PackSize = ecs->LocalProgressSpec->InSize; return S_OK; } diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/Extract.h b/3rdparty/lzma/CPP/7zip/UI/Common/Extract.h index 2904a0aba7f..03ac74b2d0b 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/Extract.h +++ b/3rdparty/lzma/CPP/7zip/UI/Common/Extract.h @@ -3,7 +3,7 @@ #ifndef __EXTRACT_H #define __EXTRACT_H -#include "Windows/FileFind.h" +#include "../../../Windows/FileFind.h" #include "../../Archive/IArchive.h" @@ -14,21 +14,37 @@ #include "../Common/LoadCodecs.h" -struct CExtractOptions +struct CExtractOptionsBase +{ + CBoolPair ElimDup; + + bool PathMode_Force; + bool OverwriteMode_Force; + NExtract::NPathMode::EEnum PathMode; + NExtract::NOverwriteMode::EEnum OverwriteMode; + + FString OutputDir; + CExtractNtOptions NtOptions; + + CExtractOptionsBase(): + PathMode_Force(false), + OverwriteMode_Force(false), + PathMode(NExtract::NPathMode::kFullPaths), + OverwriteMode(NExtract::NOverwriteMode::kAsk) + {} +}; + +struct CExtractOptions: public CExtractOptionsBase { bool StdInMode; bool StdOutMode; bool YesToAll; bool TestMode; - bool CalcCrc; - NExtract::NPathMode::EEnum PathMode; - NExtract::NOverwriteMode::EEnum OverwriteMode; - FString OutputDir; // bool ShowDialog; // bool PasswordEnabled; // UString Password; - #if !defined(_7ZIP_ST) && !defined(_SFX) + #ifndef _SFX CObjectVector<CProperty> Properties; #endif @@ -37,13 +53,10 @@ struct CExtractOptions #endif CExtractOptions(): + TestMode(false), StdInMode(false), StdOutMode(false), - YesToAll(false), - TestMode(false), - CalcCrc(false), - PathMode(NExtract::NPathMode::kFullPathnames), - OverwriteMode(NExtract::NOverwriteMode::kAskBefore) + YesToAll(false) {} }; @@ -51,26 +64,31 @@ struct CDecompressStat { UInt64 NumArchives; UInt64 UnpackSize; + UInt64 AltStreams_UnpackSize; UInt64 PackSize; UInt64 NumFolders; UInt64 NumFiles; - UInt32 CrcSum; + UInt64 NumAltStreams; void Clear() { - NumArchives = UnpackSize = PackSize = NumFolders = NumFiles = 0; - CrcSum = 0; + NumArchives = UnpackSize = AltStreams_UnpackSize = PackSize = NumFolders = NumFiles = NumAltStreams = 0; } }; -HRESULT DecompressArchives( - CCodecs *codecs, const CIntVector &formatIndices, +HRESULT Extract( + CCodecs *codecs, + const CObjectVector<COpenType> &types, + const CIntVector &excludedFormats, UStringVector &archivePaths, UStringVector &archivePathsFull, const NWildcard::CCensorNode &wildcardCensor, const CExtractOptions &options, IOpenCallbackUI *openCallback, IExtractCallbackUI *extractCallback, + #ifndef _SFX + IHashCalc *hash, + #endif UString &errorMessage, - CDecompressStat &stat); + CDecompressStat &st); #endif diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/ExtractMode.h b/3rdparty/lzma/CPP/7zip/UI/Common/ExtractMode.h index b448fb30a6e..3b2b9a02e81 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/ExtractMode.h +++ b/3rdparty/lzma/CPP/7zip/UI/Common/ExtractMode.h @@ -5,27 +5,30 @@ namespace NExtract { - namespace NPathMode +namespace NPathMode +{ + enum EEnum { - enum EEnum - { - kFullPathnames, - kCurrentPathnames, - kNoPathnames - }; - } - - namespace NOverwriteMode + kFullPaths, + kCurPaths, + kNoPaths, + kAbsPaths, + kNoPathsAlt // alt streams must be extracted without name of base file + }; +} + +namespace NOverwriteMode +{ + enum EEnum { - enum EEnum - { - kAskBefore, - kWithoutPrompt, - kSkipExisting, - kAutoRename, - kAutoRenameExisting - }; - } + kAsk, + kOverwrite, + kSkip, + kRename, + kRenameExisting + }; +} + } #endif diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/ExtractingFilePath.cpp b/3rdparty/lzma/CPP/7zip/UI/Common/ExtractingFilePath.cpp index 25494d0e642..39e67b7f2ed 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/ExtractingFilePath.cpp +++ b/3rdparty/lzma/CPP/7zip/UI/Common/ExtractingFilePath.cpp @@ -2,141 +2,236 @@ #include "StdAfx.h" -#include "../../../../C/Types.h" +#include "../../../Common/Wildcard.h" -#include "Common/Wildcard.h" +#include "../../../Windows/FileName.h" #include "ExtractingFilePath.h" -static UString ReplaceIncorrectChars(const UString &s) +static void ReplaceIncorrectChars(UString &s) { + { + for (unsigned i = 0; i < s.Len(); i++) + { + wchar_t c = s[i]; + if ( + #ifdef _WIN32 + c == ':' || c == '*' || c == '?' || c < 0x20 || c == '<' || c == '>' || c == '|' || c == '"' + || c == '/' + // || c == 0x202E // RLO + || + #endif + c == WCHAR_PATH_SEPARATOR) + s.ReplaceOneCharAtPos(i, '_'); + } + } + #ifdef _WIN32 - UString res; - for (int i = 0; i < s.Length(); i++) { - wchar_t c = s[i]; - if (c < 0x20 || c == '*' || c == '?' || c == '<' || c == '>' || c == '|' || c == ':' || c == '"') - c = '_'; - res += c; + for (unsigned i = s.Len(); i != 0;) + { + wchar_t c = s[--i]; + if (c != '.' && c != ' ') + break; + s.ReplaceOneCharAtPos(i, '_'); + } } - res.TrimRight(); - while (!res.IsEmpty() && res.Back() == '.') - res.DeleteBack(); - return res; - #else - return s; #endif } #ifdef _WIN32 -static const wchar_t *g_ReservedNames[] = -{ - L"CON", L"PRN", L"AUX", L"NUL" -}; -static bool CheckTail(const UString &name, int len) +/* WinXP-64 doesn't support ':', '\\' and '/' symbols in name of alt stream. + But colon in postfix ":$DATA" is allowed. + WIN32 functions don't allow empty alt stream name "name:" */ + +void Correct_AltStream_Name(UString &s) { - int dotPos = name.Find(L'.'); - if (dotPos < 0) - dotPos = name.Length(); - UString s = name.Left(dotPos); - s.TrimRight(); - return (s.Length() != len); + unsigned len = s.Len(); + const unsigned kPostfixSize = 6; + if (s.Len() >= kPostfixSize + && StringsAreEqualNoCase_Ascii(s.RightPtr(kPostfixSize), ":$DATA")) + len -= kPostfixSize; + for (unsigned i = 0; i < len; i++) + { + wchar_t c = s[i]; + if (c == ':' || c == '\\' || c == '/' + || c == 0x202E // RLO + ) + s.ReplaceOneCharAtPos(i, '_'); + } + if (s.IsEmpty()) + s = L'_'; } -static bool CheckNameNum(const UString &name, const wchar_t *reservedName) +static const unsigned g_ReservedWithNum_Index = 4; + +static const char * const g_ReservedNames[] = { - int len = MyStringLen(reservedName); - if (name.Length() <= len) - return true; - if (name.Left(len).CompareNoCase(reservedName) != 0) - return true; - wchar_t c = name[len]; - if (c < L'0' || c > L'9') - return true; - return CheckTail(name, len + 1); -} + "CON", "PRN", "AUX", "NUL", + "COM", "LPT" +}; static bool IsSupportedName(const UString &name) { - for (int i = 0; i < sizeof(g_ReservedNames) / sizeof(g_ReservedNames[0]); i++) + for (unsigned i = 0; i < ARRAY_SIZE(g_ReservedNames); i++) { - const wchar_t *reservedName = g_ReservedNames[i]; - int len = MyStringLen(reservedName); - if (name.Length() < len) + const char *reservedName = g_ReservedNames[i]; + unsigned len = MyStringLen(reservedName); + if (name.Len() < len) continue; - if (name.Left(len).CompareNoCase(reservedName) != 0) + if (!name.IsPrefixedBy_Ascii_NoCase(reservedName)) continue; - if (!CheckTail(name, len)) - return false; + if (i >= g_ReservedWithNum_Index) + { + wchar_t c = name[len]; + if (c < L'0' || c > L'9') + continue; + len++; + } + for (;;) + { + wchar_t c = name[len++]; + if (c == 0 || c == '.') + return false; + if (c != ' ') + break; + } } - if (!CheckNameNum(name, L"COM")) - return false; - return CheckNameNum(name, L"LPT"); + return true; } + +static void CorrectUnsupportedName(UString &name) +{ + if (!IsSupportedName(name)) + name.InsertAtFront(L'_'); +} + #endif -static UString GetCorrectFileName(const UString &path) +static void Correct_PathPart(UString &s) +{ + // "." and ".." + if (s[0] == '.' && (s[1] == 0 || s[1] == '.' && s[2] == 0)) + s.Empty(); + #ifdef _WIN32 + else + ReplaceIncorrectChars(s); + #endif +} + +// static const wchar_t *k_EmptyReplaceName = L"[]"; +static const wchar_t k_EmptyReplaceName = L'_'; + +UString Get_Correct_FsFile_Name(const UString &name) { - if (path == L".." || path == L".") - return UString(); - return ReplaceIncorrectChars(path); + UString res = name; + Correct_PathPart(res); + + #ifdef _WIN32 + CorrectUnsupportedName(res); + #endif + + if (res.IsEmpty()) + res = k_EmptyReplaceName; + return res; } -void MakeCorrectPath(UStringVector &pathParts) + +void Correct_FsPath(bool absIsAllowed, UStringVector &parts, bool isDir) { - for (int i = 0; i < pathParts.Size();) + unsigned i = 0; + + if (absIsAllowed) { - UString &s = pathParts[i]; - s = GetCorrectFileName(s); + #if defined(_WIN32) && !defined(UNDER_CE) + bool isDrive = false; + #endif + if (parts[0].IsEmpty()) + { + i = 1; + #if defined(_WIN32) && !defined(UNDER_CE) + if (parts.Size() > 1 && parts[1].IsEmpty()) + { + i = 2; + if (parts.Size() > 2 && parts[2] == L"?") + { + i = 3; + if (parts.Size() > 3 && NWindows::NFile::NName::IsDrivePath2(parts[3])) + { + isDrive = true; + i = 4; + } + } + } + #endif + } + #if defined(_WIN32) && !defined(UNDER_CE) + else if (NWindows::NFile::NName::IsDrivePath2(parts[0])) + { + isDrive = true; + i = 1; + } + + if (isDrive) + { + // we convert "c:name" to "c:\name", if absIsAllowed path. + const UString &ds = parts[i - 1]; + if (ds.Len() != 2) + { + UString s = ds.Ptr(2); + parts.Insert(i, s); + } + } + #endif + } + + for (; i < parts.Size();) + { + UString &s = parts[i]; + + Correct_PathPart(s); + if (s.IsEmpty()) - pathParts.Delete(i); + { + if (isDir || i != parts.Size() - 1) + { + parts.Delete(i); + continue; + } + s = k_EmptyReplaceName; + } else { #ifdef _WIN32 - if (!IsSupportedName(s)) - s = (UString)L"_" + s; + CorrectUnsupportedName(s); #endif - i++; } + + i++; } -} -UString MakePathNameFromParts(const UStringVector &parts) -{ - UString result; - for (int i = 0; i < parts.Size(); i++) + if (!isDir) { - if (i != 0) - result += WCHAR_PATH_SEPARATOR; - result += parts[i]; + if (parts.IsEmpty()) + parts.Add(k_EmptyReplaceName); + else + { + UString &s = parts.Back(); + if (s.IsEmpty()) + s = k_EmptyReplaceName; + } } - return result; } -UString GetCorrectFsPath(const UString &path) -{ - UString res = GetCorrectFileName(path); - #ifdef _WIN32 - if (!IsSupportedName(res)) - res = (UString)L"_" + res; - #endif - return res; -} - -UString GetCorrectFullFsPath(const UString &path) +UString MakePathFromParts(const UStringVector &parts) { - UStringVector parts; - SplitPathToParts(path, parts); - for (int i = 0; i < parts.Size(); i++) + UString s; + FOR_VECTOR (i, parts) { - UString &s = parts[i]; - #ifdef _WIN32 - while (!s.IsEmpty() && s.Back() == '.') - s.DeleteBack(); - if (!IsSupportedName(s)) - s = (UString)L"_" + s; - #endif + if (i != 0) + s.Add_PathSepar(); + s += parts[i]; } - return MakePathNameFromParts(parts); + return s; } diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/ExtractingFilePath.h b/3rdparty/lzma/CPP/7zip/UI/Common/ExtractingFilePath.h index da28bfc23b7..a52a1f4a5b4 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/ExtractingFilePath.h +++ b/3rdparty/lzma/CPP/7zip/UI/Common/ExtractingFilePath.h @@ -3,11 +3,17 @@ #ifndef __EXTRACTING_FILE_PATH_H #define __EXTRACTING_FILE_PATH_H -#include "Common/MyString.h" +#include "../../../Common/MyString.h" -UString MakePathNameFromParts(const UStringVector &parts); -void MakeCorrectPath(UStringVector &pathParts); -UString GetCorrectFsPath(const UString &path); -UString GetCorrectFullFsPath(const UString &path); +#ifdef _WIN32 +void Correct_AltStream_Name(UString &s); +#endif + +// replaces unsuported characters, and replaces "." , ".." and "" to "[]" +UString Get_Correct_FsFile_Name(const UString &name); + +void Correct_FsPath(bool absIsAllowed, UStringVector &parts, bool isDir); + +UString MakePathFromParts(const UStringVector &parts); #endif diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/HashCalc.cpp b/3rdparty/lzma/CPP/7zip/UI/Common/HashCalc.cpp new file mode 100644 index 00000000000..c33f837fe77 --- /dev/null +++ b/3rdparty/lzma/CPP/7zip/UI/Common/HashCalc.cpp @@ -0,0 +1,351 @@ +// HashCalc.cpp + +#include "StdAfx.h" + +#include "../../../../C/Alloc.h" + +#include "../../../Common/StringToInt.h" + +#include "../../Common/FileStreams.h" +#include "../../Common/StreamUtils.h" + +#include "EnumDirItems.h" +#include "HashCalc.h" + +using namespace NWindows; + +class CHashMidBuf +{ + void *_data; +public: + CHashMidBuf(): _data(0) {} + operator void *() { return _data; } + bool Alloc(size_t size) + { + if (_data != 0) + return false; + _data = ::MidAlloc(size); + return _data != 0; + } + ~CHashMidBuf() { ::MidFree(_data); } +}; + +static const char *k_DefaultHashMethod = "CRC32"; + +HRESULT CHashBundle::SetMethods(DECL_EXTERNAL_CODECS_LOC_VARS const UStringVector &hashMethods) +{ + UStringVector names = hashMethods; + if (names.IsEmpty()) + { + UString s; + s.SetFromAscii(k_DefaultHashMethod); + names.Add(s); + } + + CRecordVector<CMethodId> ids; + CObjectVector<COneMethodInfo> methods; + + unsigned i; + for (i = 0; i < names.Size(); i++) + { + COneMethodInfo m; + RINOK(m.ParseMethodFromString(names[i])); + + if (m.MethodName.IsEmpty()) + m.MethodName = k_DefaultHashMethod; + + if (m.MethodName == "*") + { + CRecordVector<CMethodId> tempMethods; + GetHashMethods(EXTERNAL_CODECS_LOC_VARS tempMethods); + methods.Clear(); + ids.Clear(); + FOR_VECTOR (t, tempMethods) + { + unsigned index = ids.AddToUniqueSorted(tempMethods[t]); + if (ids.Size() != methods.Size()) + methods.Insert(index, m); + } + break; + } + else + { + // m.MethodName.RemoveChar(L'-'); + CMethodId id; + if (!FindHashMethod(EXTERNAL_CODECS_LOC_VARS m.MethodName, id)) + return E_NOTIMPL; + unsigned index = ids.AddToUniqueSorted(id); + if (ids.Size() != methods.Size()) + methods.Insert(index, m); + } + } + + for (i = 0; i < ids.Size(); i++) + { + CMyComPtr<IHasher> hasher; + AString name; + RINOK(CreateHasher(EXTERNAL_CODECS_LOC_VARS ids[i], name, hasher)); + if (!hasher) + throw "Can't create hasher"; + const COneMethodInfo &m = methods[i]; + { + CMyComPtr<ICompressSetCoderProperties> scp; + hasher.QueryInterface(IID_ICompressSetCoderProperties, &scp); + if (scp) + RINOK(m.SetCoderProps(scp, NULL)); + } + UInt32 digestSize = hasher->GetDigestSize(); + if (digestSize > k_HashCalc_DigestSize_Max) + return E_NOTIMPL; + CHasherState &h = Hashers.AddNew(); + h.Hasher = hasher; + h.Name = name; + h.DigestSize = digestSize; + for (unsigned i = 0; i < k_HashCalc_NumGroups; i++) + memset(h.Digests[i], 0, digestSize); + } + + return S_OK; +} + +void CHashBundle::InitForNewFile() +{ + CurSize = 0; + FOR_VECTOR (i, Hashers) + { + CHasherState &h = Hashers[i]; + h.Hasher->Init(); + memset(h.Digests[k_HashCalc_Index_Current], 0, h.DigestSize); + } +} + +void CHashBundle::Update(const void *data, UInt32 size) +{ + CurSize += size; + FOR_VECTOR (i, Hashers) + Hashers[i].Hasher->Update(data, size); +} + +void CHashBundle::SetSize(UInt64 size) +{ + CurSize = size; +} + +static void AddDigests(Byte *dest, const Byte *src, UInt32 size) +{ + unsigned next = 0; + for (UInt32 i = 0; i < size; i++) + { + next += (unsigned)dest[i] + (unsigned)src[i]; + dest[i] = (Byte)next; + next >>= 8; + } +} + +void CHashBundle::Final(bool isDir, bool isAltStream, const UString &path) +{ + if (isDir) + NumDirs++; + else if (isAltStream) + { + NumAltStreams++; + AltStreamsSize += CurSize; + } + else + { + NumFiles++; + FilesSize += CurSize; + } + + Byte pre[16]; + memset(pre, 0, sizeof(pre)); + if (isDir) + pre[0] = 1; + + FOR_VECTOR (i, Hashers) + { + CHasherState &h = Hashers[i]; + if (!isDir) + { + h.Hasher->Final(h.Digests[0]); + if (!isAltStream) + AddDigests(h.Digests[k_HashCalc_Index_DataSum], h.Digests[0], h.DigestSize); + } + + h.Hasher->Init(); + h.Hasher->Update(pre, sizeof(pre)); + h.Hasher->Update(h.Digests[0], h.DigestSize); + + for (unsigned k = 0; k < path.Len(); k++) + { + wchar_t c = path[k]; + Byte temp[2] = { (Byte)(c & 0xFF), (Byte)((c >> 8) & 0xFF) }; + h.Hasher->Update(temp, 2); + } + + Byte tempDigest[k_HashCalc_DigestSize_Max]; + h.Hasher->Final(tempDigest); + if (!isAltStream) + AddDigests(h.Digests[k_HashCalc_Index_NamesSum], tempDigest, h.DigestSize); + AddDigests(h.Digests[k_HashCalc_Index_StreamsSum], tempDigest, h.DigestSize); + } +} + + +HRESULT HashCalc( + DECL_EXTERNAL_CODECS_LOC_VARS + const NWildcard::CCensor &censor, + const CHashOptions &options, + AString &errorInfo, + IHashCallbackUI *callback) +{ + CDirItems dirItems; + dirItems.Callback = callback; + + if (options.StdInMode) + { + CDirItem di; + di.Size = (UInt64)(Int64)-1; + di.Attrib = 0; + di.MTime.dwLowDateTime = 0; + di.MTime.dwHighDateTime = 0; + di.CTime = di.ATime = di.MTime; + dirItems.Items.Add(di); + } + else + { + RINOK(callback->StartScanning()); + dirItems.ScanAltStreams = options.AltStreamsMode; + + HRESULT res = EnumerateItems(censor, + options.PathMode, + UString(), + dirItems); + + if (res != S_OK) + { + if (res != E_ABORT) + errorInfo = "Scanning error"; + return res; + } + RINOK(callback->FinishScanning(dirItems.Stat)); + } + + unsigned i; + CHashBundle hb; + RINOK(hb.SetMethods(EXTERNAL_CODECS_LOC_VARS options.Methods)); + hb.Init(); + + hb.NumErrors = dirItems.Stat.NumErrors; + + if (options.StdInMode) + { + RINOK(callback->SetNumFiles(1)); + } + else + { + RINOK(callback->SetTotal(dirItems.Stat.GetTotalBytes())); + } + + const UInt32 kBufSize = 1 << 15; + CHashMidBuf buf; + if (!buf.Alloc(kBufSize)) + return E_OUTOFMEMORY; + + UInt64 completeValue = 0; + + RINOK(callback->BeforeFirstFile(hb)); + + for (i = 0; i < dirItems.Items.Size(); i++) + { + CMyComPtr<ISequentialInStream> inStream; + UString path; + bool isDir = false; + bool isAltStream = false; + if (options.StdInMode) + { + inStream = new CStdInFileStream; + } + else + { + CInFileStream *inStreamSpec = new CInFileStream; + inStream = inStreamSpec; + const CDirItem &dirItem = dirItems.Items[i]; + isDir = dirItem.IsDir(); + isAltStream = dirItem.IsAltStream; + path = dirItems.GetLogPath(i); + if (!isDir) + { + FString phyPath = dirItems.GetPhyPath(i); + if (!inStreamSpec->OpenShared(phyPath, options.OpenShareForWrite)) + { + HRESULT res = callback->OpenFileError(phyPath, ::GetLastError()); + hb.NumErrors++; + if (res != S_FALSE) + return res; + continue; + } + } + } + RINOK(callback->GetStream(path, isDir)); + UInt64 fileSize = 0; + + hb.InitForNewFile(); + if (!isDir) + { + for (UInt32 step = 0;; step++) + { + if ((step & 0xFF) == 0) + RINOK(callback->SetCompleted(&completeValue)); + UInt32 size; + RINOK(inStream->Read(buf, kBufSize, &size)); + if (size == 0) + break; + hb.Update(buf, size); + fileSize += size; + completeValue += size; + } + } + hb.Final(isDir, isAltStream, path); + RINOK(callback->SetOperationResult(fileSize, hb, !isDir)); + RINOK(callback->SetCompleted(&completeValue)); + } + return callback->AfterLastFile(hb); +} + + +static inline char GetHex(unsigned v) +{ + return (char)((v < 10) ? ('0' + v) : ('A' + (v - 10))); +} + +void AddHashHexToString(char *dest, const Byte *data, UInt32 size) +{ + dest[size * 2] = 0; + + if (!data) + { + for (UInt32 i = 0; i < size; i++) + { + dest[0] = ' '; + dest[1] = ' '; + dest += 2; + } + return; + } + + int step = 2; + if (size <= 8) + { + step = -2; + dest += size * 2 - 2; + } + + for (UInt32 i = 0; i < size; i++) + { + unsigned b = data[i]; + dest[0] = GetHex((b >> 4) & 0xF); + dest[1] = GetHex(b & 0xF); + dest += step; + } +} diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/HashCalc.h b/3rdparty/lzma/CPP/7zip/UI/Common/HashCalc.h new file mode 100644 index 00000000000..38908e2e8b3 --- /dev/null +++ b/3rdparty/lzma/CPP/7zip/UI/Common/HashCalc.h @@ -0,0 +1,107 @@ +// HashCalc.h + +#ifndef __HASH_CALC_H +#define __HASH_CALC_H + +#include "../../../Common/Wildcard.h" + +#include "../../Common/CreateCoder.h" +#include "../../Common/MethodProps.h" + +#include "DirItem.h" +#include "Property.h" + +const unsigned k_HashCalc_DigestSize_Max = 64; + +const unsigned k_HashCalc_NumGroups = 4; + +enum +{ + k_HashCalc_Index_Current, + k_HashCalc_Index_DataSum, + k_HashCalc_Index_NamesSum, + k_HashCalc_Index_StreamsSum +}; + +struct CHasherState +{ + CMyComPtr<IHasher> Hasher; + AString Name; + UInt32 DigestSize; + Byte Digests[k_HashCalc_NumGroups][k_HashCalc_DigestSize_Max]; +}; + +struct IHashCalc +{ + virtual void InitForNewFile() = 0; + virtual void Update(const void *data, UInt32 size) = 0; + virtual void SetSize(UInt64 size) = 0; + virtual void Final(bool isDir, bool isAltStream, const UString &path) = 0; +}; + +struct CHashBundle: public IHashCalc +{ + CObjectVector<CHasherState> Hashers; + + UInt64 NumDirs; + UInt64 NumFiles; + UInt64 NumAltStreams; + UInt64 FilesSize; + UInt64 AltStreamsSize; + UInt64 NumErrors; + + UInt64 CurSize; + + HRESULT SetMethods(DECL_EXTERNAL_CODECS_LOC_VARS const UStringVector &methods); + + void Init() + { + NumDirs = NumFiles = NumAltStreams = FilesSize = AltStreamsSize = NumErrors = 0; + } + + void InitForNewFile(); + void Update(const void *data, UInt32 size); + void SetSize(UInt64 size); + void Final(bool isDir, bool isAltStream, const UString &path); +}; + +#define INTERFACE_IHashCallbackUI(x) \ + INTERFACE_IDirItemsCallback(x) \ + virtual HRESULT StartScanning() x; \ + virtual HRESULT FinishScanning(const CDirItemsStat &st) x; \ + virtual HRESULT SetNumFiles(UInt64 numFiles) x; \ + virtual HRESULT SetTotal(UInt64 size) x; \ + virtual HRESULT SetCompleted(const UInt64 *completeValue) x; \ + virtual HRESULT CheckBreak() x; \ + virtual HRESULT BeforeFirstFile(const CHashBundle &hb) x; \ + virtual HRESULT GetStream(const wchar_t *name, bool isFolder) x; \ + virtual HRESULT OpenFileError(const FString &path, DWORD systemError) x; \ + virtual HRESULT SetOperationResult(UInt64 fileSize, const CHashBundle &hb, bool showHash) x; \ + virtual HRESULT AfterLastFile(const CHashBundle &hb) x; \ + +struct IHashCallbackUI: public IDirItemsCallback +{ + INTERFACE_IHashCallbackUI(=0) +}; + +struct CHashOptions +{ + UStringVector Methods; + bool OpenShareForWrite; + bool StdInMode; + bool AltStreamsMode; + NWildcard::ECensorPathMode PathMode; + + CHashOptions(): StdInMode(false), OpenShareForWrite(false), AltStreamsMode(false), PathMode(NWildcard::k_RelatPath) {}; +}; + +HRESULT HashCalc( + DECL_EXTERNAL_CODECS_LOC_VARS + const NWildcard::CCensor &censor, + const CHashOptions &options, + AString &errorInfo, + IHashCallbackUI *callback); + +void AddHashHexToString(char *dest, const Byte *data, UInt32 size); + +#endif diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/IFileExtractCallback.h b/3rdparty/lzma/CPP/7zip/UI/Common/IFileExtractCallback.h index e8dcdce5fa5..c456c862737 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/IFileExtractCallback.h +++ b/3rdparty/lzma/CPP/7zip/UI/Common/IFileExtractCallback.h @@ -1,11 +1,15 @@ // IFileExtractCallback.h -#ifndef __IFILEEXTRACTCALLBACK_H -#define __IFILEEXTRACTCALLBACK_H +#ifndef __I_FILE_EXTRACT_CALLBACK_H +#define __I_FILE_EXTRACT_CALLBACK_H + +#include "../../../Common/MyString.h" -#include "Common/MyString.h" #include "../../IDecl.h" +#include "LoadCodecs.h" +#include "OpenArchive.h" + namespace NOverwriteAnswer { enum EEnum @@ -19,28 +23,92 @@ namespace NOverwriteAnswer }; } + +/* ---------- IFolderArchiveExtractCallback ---------- +is implemented by + Console/ExtractCallbackConsole.h CExtractCallbackConsole + FileManager/ExtractCallback.h CExtractCallbackImp + FAR/ExtractEngine.cpp CExtractCallBackImp: (QueryInterface is not supported) + +IID_IFolderArchiveExtractCallback is requested by: + - Agent/ArchiveFolder.cpp + CAgentFolder::CopyTo(..., IFolderOperationsExtractCallback *callback) + is sent to IArchiveFolder::Extract() + + - FileManager/PanelCopy.cpp + CPanel::CopyTo(), if (options->testMode) + is sent to IArchiveFolder::Extract() + + IFolderArchiveExtractCallback is used by Common/ArchiveExtractCallback.cpp +*/ + +#define INTERFACE_IFolderArchiveExtractCallback(x) \ + STDMETHOD(AskOverwrite)( \ + const wchar_t *existName, const FILETIME *existTime, const UInt64 *existSize, \ + const wchar_t *newName, const FILETIME *newTime, const UInt64 *newSize, \ + Int32 *answer) x; \ + STDMETHOD(PrepareOperation)(const wchar_t *name, Int32 isFolder, Int32 askExtractMode, const UInt64 *position) x; \ + STDMETHOD(MessageError)(const wchar_t *message) x; \ + STDMETHOD(SetOperationResult)(Int32 opRes, Int32 encrypted) x; \ + DECL_INTERFACE_SUB(IFolderArchiveExtractCallback, IProgress, 0x01, 0x07) { -public: - STDMETHOD(AskOverwrite)( - const wchar_t *existName, const FILETIME *existTime, const UInt64 *existSize, - const wchar_t *newName, const FILETIME *newTime, const UInt64 *newSize, - Int32 *answer) PURE; - STDMETHOD(PrepareOperation)(const wchar_t *name, bool isFolder, Int32 askExtractMode, const UInt64 *position) PURE; - STDMETHOD(MessageError)(const wchar_t *message) PURE; - STDMETHOD(SetOperationResult)(Int32 operationResult, bool encrypted) PURE; + INTERFACE_IFolderArchiveExtractCallback(PURE) }; +#define INTERFACE_IFolderArchiveExtractCallback2(x) \ + STDMETHOD(ReportExtractResult)(Int32 opRes, Int32 encrypted, const wchar_t *name) x; \ + +DECL_INTERFACE_SUB(IFolderArchiveExtractCallback2, IUnknown, 0x01, 0x08) +{ + INTERFACE_IFolderArchiveExtractCallback2(PURE) +}; + +/* ---------- IExtractCallbackUI ---------- +is implemented by + Console/ExtractCallbackConsole.h CExtractCallbackConsole + FileManager/ExtractCallback.h CExtractCallbackImp +*/ + +#ifdef _NO_CRYPTO + #define INTERFACE_IExtractCallbackUI_Crypto(x) +#else + #define INTERFACE_IExtractCallbackUI_Crypto(x) \ + virtual HRESULT SetPassword(const UString &password) x; +#endif + +#define INTERFACE_IExtractCallbackUI(x) \ + virtual HRESULT BeforeOpen(const wchar_t *name, bool testMode) x; \ + virtual HRESULT OpenResult(const CCodecs *codecs, const CArchiveLink &arcLink, const wchar_t *name, HRESULT result) x; \ + virtual HRESULT ThereAreNoFiles() x; \ + virtual HRESULT ExtractResult(HRESULT result) x; \ + INTERFACE_IExtractCallbackUI_Crypto(x) + struct IExtractCallbackUI: IFolderArchiveExtractCallback { - virtual HRESULT BeforeOpen(const wchar_t *name) = 0; - virtual HRESULT OpenResult(const wchar_t *name, HRESULT result, bool encrypted) = 0; - virtual HRESULT ThereAreNoFiles() = 0; - virtual HRESULT ExtractResult(HRESULT result) = 0; - - #ifndef _NO_CRYPTO - virtual HRESULT SetPassword(const UString &password) = 0; - #endif + INTERFACE_IExtractCallbackUI(PURE) }; + + +#define INTERFACE_IGetProp(x) \ + STDMETHOD(GetProp)(PROPID propID, PROPVARIANT *value) x; \ + +DECL_INTERFACE_SUB(IGetProp, IUnknown, 0x01, 0x20) +{ + INTERFACE_IGetProp(PURE) +}; + +#define INTERFACE_IFolderExtractToStreamCallback(x) \ + STDMETHOD(UseExtractToStream)(Int32 *res) x; \ + STDMETHOD(GetStream7)(const wchar_t *name, Int32 isDir, ISequentialOutStream **outStream, Int32 askExtractMode, IGetProp *getProp) x; \ + STDMETHOD(PrepareOperation7)(Int32 askExtractMode) x; \ + STDMETHOD(SetOperationResult7)(Int32 resultEOperationResult, Int32 encrypted) x; \ + +DECL_INTERFACE_SUB(IFolderExtractToStreamCallback, IUnknown, 0x01, 0x30) +{ + INTERFACE_IFolderExtractToStreamCallback(PURE) +}; + + #endif diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/LoadCodecs.cpp b/3rdparty/lzma/CPP/7zip/UI/Common/LoadCodecs.cpp index cb4f83ee7a1..5435499baa8 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/LoadCodecs.cpp +++ b/3rdparty/lzma/CPP/7zip/UI/Common/LoadCodecs.cpp @@ -1,41 +1,93 @@ // LoadCodecs.cpp +/* +EXTERNAL_CODECS +--------------- + CCodecs::Load() tries to detect the directory with plugins. + It stops the checking, if it can find any of the following items: + - 7z.dll file + - "Formats" subdir + - "Codecs" subdir + The order of check: + 1) directory of client executable + 2) WIN32: directory for REGISTRY item [HKEY_*\Software\7-Zip\Path**] + The order for HKEY_* : Path** : + - HKEY_CURRENT_USER : PathXX + - HKEY_LOCAL_MACHINE : PathXX + - HKEY_CURRENT_USER : Path + - HKEY_LOCAL_MACHINE : Path + PathXX is Path32 in 32-bit code + PathXX is Path64 in 64-bit code + + +EXPORT_CODECS +------------- + if (EXTERNAL_CODECS) is defined, then the code exports internal + codecs of client from CCodecs object to external plugins. + 7-Zip doesn't use that feature. 7-Zip uses the scheme: + - client application without internal plugins. + - 7z.dll module contains all (or almost all) plugins. + 7z.dll can use codecs from another plugins, if required. +*/ + + #include "StdAfx.h" -#include "LoadCodecs.h" +#include "../../../../C/7zVersion.h" #include "../../../Common/MyCom.h" +#include "../../../Common/StringToInt.h" +#include "../../../Common/StringConvert.h" + +#include "../../../Windows/PropVariant.h" + +#include "LoadCodecs.h" + +using namespace NWindows; + #ifdef NEW_FOLDER_INTERFACE #include "../../../Common/StringToInt.h" #endif -#include "../../../Windows/PropVariant.h" #include "../../ICoder.h" #include "../../Common/RegisterArc.h" #ifdef EXTERNAL_CODECS -#include "../../../Windows/FileFind.h" -#include "../../../Windows/DLL.h" + +// #define EXPORT_CODECS + +#endif + #ifdef NEW_FOLDER_INTERFACE +extern HINSTANCE g_hInstance; #include "../../../Windows/ResourceString.h" static const UINT kIconTypesResId = 100; #endif +#ifdef EXTERNAL_CODECS + +#include "../../../Windows/FileFind.h" +#include "../../../Windows/DLL.h" + #ifdef _WIN32 -#include "Windows/FileName.h" -#include "Windows/Registry.h" +#include "../../../Windows/FileName.h" +#include "../../../Windows/Registry.h" #endif -using namespace NWindows; using namespace NFile; -#ifdef _WIN32 -extern HINSTANCE g_hInstance; -#endif #define kCodecsFolderName FTEXT("Codecs") #define kFormatsFolderName FTEXT("Formats") -static CFSTR kMainDll = FTEXT("7z.dll"); + + +static CFSTR kMainDll = + // #ifdef _WIN32 + FTEXT("7z.dll"); + // #else + // FTEXT("7z.so"); + // #endif + #ifdef _WIN32 @@ -64,7 +116,97 @@ static bool ReadPathFromRegistry(HKEY baseKey, LPCWSTR value, FString &path) return false; } -#endif +#endif // _WIN32 + +#endif // EXTERNAL_CODECS + + +static const unsigned kNumArcsMax = 64; +static unsigned g_NumArcs = 0; +static const CArcInfo *g_Arcs[kNumArcsMax]; + +void RegisterArc(const CArcInfo *arcInfo) throw() +{ + if (g_NumArcs < kNumArcsMax) + { + g_Arcs[g_NumArcs] = arcInfo; + g_NumArcs++; + } +} + +static void SplitString(const UString &srcString, UStringVector &destStrings) +{ + destStrings.Clear(); + UString s; + unsigned len = srcString.Len(); + if (len == 0) + return; + for (unsigned i = 0; i < len; i++) + { + wchar_t c = srcString[i]; + if (c == L' ') + { + if (!s.IsEmpty()) + { + destStrings.Add(s); + s.Empty(); + } + } + else + s += c; + } + if (!s.IsEmpty()) + destStrings.Add(s); +} + +int CArcInfoEx::FindExtension(const UString &ext) const +{ + FOR_VECTOR (i, Exts) + if (ext.IsEqualTo_NoCase(Exts[i].Ext)) + return i; + return -1; +} + +void CArcInfoEx::AddExts(const UString &ext, const UString &addExt) +{ + UStringVector exts, addExts; + SplitString(ext, exts); + SplitString(addExt, addExts); + FOR_VECTOR (i, exts) + { + CArcExtInfo extInfo; + extInfo.Ext = exts[i]; + if (i < addExts.Size()) + { + extInfo.AddExt = addExts[i]; + if (extInfo.AddExt == L"*") + extInfo.AddExt.Empty(); + } + Exts.Add(extInfo); + } +} + +#ifndef _SFX + +static bool ParseSignatures(const Byte *data, unsigned size, CObjectVector<CByteBuffer> &signatures) +{ + signatures.Clear(); + while (size > 0) + { + unsigned len = *data++; + size--; + if (len > size) + return false; + signatures.AddNew().CopyFrom(data, len); + data += len; + size -= len; + } + return true; +} + +#endif // _SFX + +#ifdef EXTERNAL_CODECS static FString GetBaseFolderPrefixFromRegistry() { @@ -84,22 +226,17 @@ static FString GetBaseFolderPrefixFromRegistry() return moduleFolderPrefix; } -typedef UInt32 (WINAPI *GetNumberOfMethodsFunc)(UInt32 *numMethods); -typedef UInt32 (WINAPI *GetNumberOfFormatsFunc)(UInt32 *numFormats); -typedef UInt32 (WINAPI *GetHandlerPropertyFunc)(PROPID propID, PROPVARIANT *value); -typedef UInt32 (WINAPI *GetHandlerPropertyFunc2)(UInt32 index, PROPID propID, PROPVARIANT *value); -typedef UInt32 (WINAPI *CreateObjectFunc)(const GUID *clsID, const GUID *iid, void **outObject); -typedef UInt32 (WINAPI *SetLargePageModeFunc)(); - -static HRESULT GetCoderClass(GetMethodPropertyFunc getMethodProperty, UInt32 index, +static HRESULT GetCoderClass(Func_GetMethodProperty getMethodProperty, UInt32 index, PROPID propId, CLSID &clsId, bool &isAssigned) { - NWindows::NCOM::CPropVariant prop; + NCOM::CPropVariant prop; isAssigned = false; RINOK(getMethodProperty(index, propId, &prop)); if (prop.vt == VT_BSTR) { + if (::SysStringByteLen(prop.bstrVal) != sizeof(GUID)) + return E_FAIL; isAssigned = true; clsId = *(const GUID *)prop.bstrVal; } @@ -111,34 +248,53 @@ static HRESULT GetCoderClass(GetMethodPropertyFunc getMethodProperty, UInt32 ind HRESULT CCodecs::LoadCodecs() { CCodecLib &lib = Libs.Back(); - lib.GetMethodProperty = (GetMethodPropertyFunc)lib.Lib.GetProc("GetMethodProperty"); - if (lib.GetMethodProperty == NULL) - return S_OK; - UInt32 numMethods = 1; - GetNumberOfMethodsFunc getNumberOfMethodsFunc = (GetNumberOfMethodsFunc)lib.Lib.GetProc("GetNumberOfMethods"); - if (getNumberOfMethodsFunc != NULL) + lib.CreateDecoder = (Func_CreateDecoder)lib.Lib.GetProc("CreateDecoder"); + lib.CreateEncoder = (Func_CreateEncoder)lib.Lib.GetProc("CreateEncoder"); + lib.GetMethodProperty = (Func_GetMethodProperty)lib.Lib.GetProc("GetMethodProperty"); + + if (lib.GetMethodProperty) { - RINOK(getNumberOfMethodsFunc(&numMethods)); + UInt32 numMethods = 1; + Func_GetNumberOfMethods getNumberOfMethods = (Func_GetNumberOfMethods)lib.Lib.GetProc("GetNumberOfMethods"); + if (getNumberOfMethods) + { + RINOK(getNumberOfMethods(&numMethods)); + } + for (UInt32 i = 0; i < numMethods; i++) + { + CDllCodecInfo info; + info.LibIndex = Libs.Size() - 1; + info.CodecIndex = i; + RINOK(GetCoderClass(lib.GetMethodProperty, i, NMethodPropID::kEncoder, info.Encoder, info.EncoderIsAssigned)); + RINOK(GetCoderClass(lib.GetMethodProperty, i, NMethodPropID::kDecoder, info.Decoder, info.DecoderIsAssigned)); + Codecs.Add(info); + } } - for(UInt32 i = 0; i < numMethods; i++) + Func_GetHashers getHashers = (Func_GetHashers)lib.Lib.GetProc("GetHashers"); + if (getHashers) { - CDllCodecInfo info; - info.LibIndex = Libs.Size() - 1; - info.CodecIndex = i; - - RINOK(GetCoderClass(lib.GetMethodProperty, i, NMethodPropID::kEncoder, info.Encoder, info.EncoderIsAssigned)); - RINOK(GetCoderClass(lib.GetMethodProperty, i, NMethodPropID::kDecoder, info.Decoder, info.DecoderIsAssigned)); - - Codecs.Add(info); + RINOK(getHashers(&lib.ComHashers)); + if (lib.ComHashers) + { + UInt32 numMethods = lib.ComHashers->GetNumHashers(); + for (UInt32 i = 0; i < numMethods; i++) + { + CDllHasherInfo info; + info.LibIndex = Libs.Size() - 1; + info.HasherIndex = i; + Hashers.Add(info); + } + } } + return S_OK; } -static HRESULT ReadProp( - GetHandlerPropertyFunc getProp, - GetHandlerPropertyFunc2 getProp2, +static HRESULT GetProp( + Func_GetHandlerProperty getProp, + Func_GetHandlerProperty2 getProp2, UInt32 index, PROPID propID, NCOM::CPropVariant &prop) { if (getProp2) @@ -146,13 +302,14 @@ static HRESULT ReadProp( return getProp(propID, &prop); } -static HRESULT ReadBoolProp( - GetHandlerPropertyFunc getProp, - GetHandlerPropertyFunc2 getProp2, +static HRESULT GetProp_Bool( + Func_GetHandlerProperty getProp, + Func_GetHandlerProperty2 getProp2, UInt32 index, PROPID propID, bool &res) { + res = false; NCOM::CPropVariant prop; - RINOK(ReadProp(getProp, getProp2, index, propID, prop)); + RINOK(GetProp(getProp, getProp2, index, propID, prop)); if (prop.vt == VT_BOOL) res = VARIANT_BOOLToBool(prop.boolVal); else if (prop.vt != VT_EMPTY) @@ -160,190 +317,154 @@ static HRESULT ReadBoolProp( return S_OK; } -static HRESULT ReadStringProp( - GetHandlerPropertyFunc getProp, - GetHandlerPropertyFunc2 getProp2, - UInt32 index, PROPID propID, UString &res) +static HRESULT GetProp_UInt32( + Func_GetHandlerProperty getProp, + Func_GetHandlerProperty2 getProp2, + UInt32 index, PROPID propID, UInt32 &res, bool &defined) { + res = 0; + defined = false; NCOM::CPropVariant prop; - RINOK(ReadProp(getProp, getProp2, index, propID, prop)); - if (prop.vt == VT_BSTR) - res = prop.bstrVal; + RINOK(GetProp(getProp, getProp2, index, propID, prop)); + if (prop.vt == VT_UI4) + { + res = prop.ulVal; + defined = true; + } else if (prop.vt != VT_EMPTY) return E_FAIL; return S_OK; } -#endif - -static const unsigned int kNumArcsMax = 48; -static unsigned int g_NumArcs = 0; -static const CArcInfo *g_Arcs[kNumArcsMax]; -void RegisterArc(const CArcInfo *arcInfo) +static HRESULT GetProp_String( + Func_GetHandlerProperty getProp, + Func_GetHandlerProperty2 getProp2, + UInt32 index, PROPID propID, UString &res) { - if (g_NumArcs < kNumArcsMax) - g_Arcs[g_NumArcs++] = arcInfo; + res.Empty(); + NCOM::CPropVariant prop; + RINOK(GetProp(getProp, getProp2, index, propID, prop)); + if (prop.vt == VT_BSTR) + res.SetFromBstr(prop.bstrVal); + else if (prop.vt != VT_EMPTY) + return E_FAIL; + return S_OK; } -static void SplitString(const UString &srcString, UStringVector &destStrings) +static HRESULT GetProp_RawData( + Func_GetHandlerProperty getProp, + Func_GetHandlerProperty2 getProp2, + UInt32 index, PROPID propID, CByteBuffer &bb) { - destStrings.Clear(); - UString s; - int len = srcString.Length(); - if (len == 0) - return; - for (int i = 0; i < len; i++) + bb.Free(); + NCOM::CPropVariant prop; + RINOK(GetProp(getProp, getProp2, index, propID, prop)); + if (prop.vt == VT_BSTR) { - wchar_t c = srcString[i]; - if (c == L' ') - { - if (!s.IsEmpty()) - { - destStrings.Add(s); - s.Empty(); - } - } - else - s += c; + UINT len = ::SysStringByteLen(prop.bstrVal); + bb.CopyFrom((const Byte *)prop.bstrVal, len); } - if (!s.IsEmpty()) - destStrings.Add(s); + else if (prop.vt != VT_EMPTY) + return E_FAIL; + return S_OK; } -int CArcInfoEx::FindExtension(const UString &ext) const +static const UInt32 kArcFlagsPars[] = { - for (int i = 0; i < Exts.Size(); i++) - if (ext.CompareNoCase(Exts[i].Ext) == 0) - return i; - return -1; -} + NArchive::NHandlerPropID::kKeepName, NArcInfoFlags::kKeepName, + NArchive::NHandlerPropID::kAltStreams, NArcInfoFlags::kAltStreams, + NArchive::NHandlerPropID::kNtSecure, NArcInfoFlags::kNtSecure +}; -void CArcInfoEx::AddExts(const wchar_t *ext, const wchar_t *addExt) +HRESULT CCodecs::LoadFormats() { - UStringVector exts, addExts; - if (ext != 0) - SplitString(ext, exts); - if (addExt != 0) - SplitString(addExt, addExts); - for (int i = 0; i < exts.Size(); i++) + const NDLL::CLibrary &lib = Libs.Back().Lib; + + Func_GetHandlerProperty getProp = NULL; + Func_GetHandlerProperty2 getProp2 = (Func_GetHandlerProperty2)lib.GetProc("GetHandlerProperty2"); + Func_GetIsArc getIsArc = (Func_GetIsArc)lib.GetProc("GetIsArc"); + + UInt32 numFormats = 1; + + if (getProp2) { - CArcExtInfo extInfo; - extInfo.Ext = exts[i]; - if (i < addExts.Size()) + Func_GetNumberOfFormats getNumberOfFormats = (Func_GetNumberOfFormats)lib.GetProc("GetNumberOfFormats"); + if (getNumberOfFormats) { - extInfo.AddExt = addExts[i]; - if (extInfo.AddExt == L"*") - extInfo.AddExt.Empty(); + RINOK(getNumberOfFormats(&numFormats)); } - Exts.Add(extInfo); } -} - -#ifdef EXTERNAL_CODECS - -HRESULT CCodecs::LoadFormats() -{ - const NDLL::CLibrary &lib = Libs.Back().Lib; - GetHandlerPropertyFunc getProp = 0; - GetHandlerPropertyFunc2 getProp2 = (GetHandlerPropertyFunc2)lib.GetProc("GetHandlerProperty2"); - if (getProp2 == NULL) + else { - getProp = (GetHandlerPropertyFunc)lib.GetProc("GetHandlerProperty"); - if (getProp == NULL) + getProp = (Func_GetHandlerProperty)lib.GetProc("GetHandlerProperty"); + if (!getProp) return S_OK; } - - UInt32 numFormats = 1; - GetNumberOfFormatsFunc getNumberOfFormats = (GetNumberOfFormatsFunc)lib.GetProc("GetNumberOfFormats"); - if (getNumberOfFormats != NULL) - { - RINOK(getNumberOfFormats(&numFormats)); - } - if (getProp2 == NULL) - numFormats = 1; - - for(UInt32 i = 0; i < numFormats; i++) + + for (UInt32 i = 0; i < numFormats; i++) { CArcInfoEx item; item.LibIndex = Libs.Size() - 1; item.FormatIndex = i; - RINOK(ReadStringProp(getProp, getProp2, i, NArchive::kName, item.Name)); + RINOK(GetProp_String(getProp, getProp2, i, NArchive::NHandlerPropID::kName, item.Name)); - NCOM::CPropVariant prop; - if (ReadProp(getProp, getProp2, i, NArchive::kClassID, prop) != S_OK) - continue; - if (prop.vt != VT_BSTR) - continue; - item.ClassID = *(const GUID *)prop.bstrVal; - prop.Clear(); + { + NCOM::CPropVariant prop; + if (GetProp(getProp, getProp2, i, NArchive::NHandlerPropID::kClassID, prop) != S_OK) + continue; + if (prop.vt != VT_BSTR) + continue; + if (::SysStringByteLen(prop.bstrVal) != sizeof(GUID)) + return E_FAIL; + item.ClassID = *(const GUID *)prop.bstrVal; + prop.Clear(); + } UString ext, addExt; - RINOK(ReadStringProp(getProp, getProp2, i, NArchive::kExtension, ext)); - RINOK(ReadStringProp(getProp, getProp2, i, NArchive::kAddExtension, addExt)); + RINOK(GetProp_String(getProp, getProp2, i, NArchive::NHandlerPropID::kExtension, ext)); + RINOK(GetProp_String(getProp, getProp2, i, NArchive::NHandlerPropID::kAddExtension, addExt)); item.AddExts(ext, addExt); - ReadBoolProp(getProp, getProp2, i, NArchive::kUpdate, item.UpdateEnabled); - if (item.UpdateEnabled) - ReadBoolProp(getProp, getProp2, i, NArchive::kKeepName, item.KeepName); - - if (ReadProp(getProp, getProp2, i, NArchive::kStartSignature, prop) == S_OK) - if (prop.vt == VT_BSTR) - { - UINT len = ::SysStringByteLen(prop.bstrVal); - item.StartSignature.SetCapacity(len); - memmove(item.StartSignature, prop.bstrVal, len); - } - Formats.Add(item); - } - return S_OK; -} - -#ifdef NEW_FOLDER_INTERFACE -void CCodecIcons::LoadIcons(HMODULE m) -{ - UString iconTypes = MyLoadStringW(m, kIconTypesResId); - UStringVector pairs; - SplitString(iconTypes, pairs); - for (int i = 0; i < pairs.Size(); i++) - { - const UString &s = pairs[i]; - int pos = s.Find(L':'); - CIconPair iconPair; - iconPair.IconIndex = -1; - if (pos < 0) - pos = s.Length(); - else + GetProp_Bool(getProp, getProp2, i, NArchive::NHandlerPropID::kUpdate, item.UpdateEnabled); + bool flags_Defined = false; + RINOK(GetProp_UInt32(getProp, getProp2, i, NArchive::NHandlerPropID::kFlags, item.Flags, flags_Defined)); + item.NewInterface = flags_Defined; + if (!flags_Defined) // && item.UpdateEnabled { - UString num = s.Mid(pos + 1); - if (!num.IsEmpty()) + // support for DLL version before 9.31: + for (unsigned j = 0; j < ARRAY_SIZE(kArcFlagsPars); j += 2) { - const wchar_t *end; - iconPair.IconIndex = (UInt32)ConvertStringToUInt64(num, &end); - if (*end != L'\0') - continue; + bool val = false; + GetProp_Bool(getProp, getProp2, i, kArcFlagsPars[j], val); + if (val) + item.Flags |= kArcFlagsPars[j + 1]; } } - iconPair.Ext = s.Left(pos); - IconPairs.Add(iconPair); - } -} - -bool CCodecIcons::FindIconIndex(const UString &ext, int &iconIndex) const -{ - iconIndex = -1; - for (int i = 0; i < IconPairs.Size(); i++) - { - const CIconPair &pair = IconPairs[i]; - if (ext.CompareNoCase(pair.Ext) == 0) + + CByteBuffer sig; + RINOK(GetProp_RawData(getProp, getProp2, i, NArchive::NHandlerPropID::kSignature, sig)); + if (sig.Size() != 0) + item.Signatures.Add(sig); + else { - iconIndex = pair.IconIndex; - return true; + RINOK(GetProp_RawData(getProp, getProp2, i, NArchive::NHandlerPropID::kMultiSignature, sig)); + ParseSignatures(sig, (unsigned)sig.Size(), item.Signatures); } + + bool signatureOffset_Defined; + RINOK(GetProp_UInt32(getProp, getProp2, i, NArchive::NHandlerPropID::kSignatureOffset, item.SignatureOffset, signatureOffset_Defined)); + + // bool version_Defined; + // RINOK(GetProp_UInt32(getProp, getProp2, i, NArchive::NHandlerPropID::kVersion, item.Version, version_Defined)); + + if (getIsArc) + getIsArc(i, &item.IsArcFunc); + + Formats.Add(item); } - return false; + return S_OK; } -#endif #ifdef _7ZIP_LARGE_PAGES extern "C" @@ -352,23 +473,28 @@ extern "C" } #endif -HRESULT CCodecs::LoadDll(const FString &dllPath, bool needCheckDll) +HRESULT CCodecs::LoadDll(const FString &dllPath, bool needCheckDll, bool *loadedOK) { + if (loadedOK) + *loadedOK = false; + if (needCheckDll) { - NDLL::CLibrary library; - if (!library.LoadEx(dllPath, LOAD_LIBRARY_AS_DATAFILE)) + NDLL::CLibrary lib; + if (!lib.LoadEx(dllPath, LOAD_LIBRARY_AS_DATAFILE)) return S_OK; } - Libs.Add(CCodecLib()); + + Libs.AddNew(); CCodecLib &lib = Libs.Back(); - #ifdef NEW_FOLDER_INTERFACE lib.Path = dllPath; - #endif bool used = false; HRESULT res = S_OK; + if (lib.Lib.Load(dllPath)) { + if (loadedOK) + *loadedOK = true; #ifdef NEW_FOLDER_INTERFACE lib.LoadIcons(); #endif @@ -376,28 +502,38 @@ HRESULT CCodecs::LoadDll(const FString &dllPath, bool needCheckDll) #ifdef _7ZIP_LARGE_PAGES if (g_LargePageSize != 0) { - SetLargePageModeFunc setLargePageMode = (SetLargePageModeFunc)lib.Lib.GetProc("SetLargePageMode"); - if (setLargePageMode != 0) + Func_SetLargePageMode setLargePageMode = (Func_SetLargePageMode)lib.Lib.GetProc("SetLargePageMode"); + if (setLargePageMode) setLargePageMode(); } #endif - lib.CreateObject = (CreateObjectFunc)lib.Lib.GetProc("CreateObject"); - if (lib.CreateObject != 0) + if (CaseSensitiveChange) { - int startSize = Codecs.Size(); + Func_SetCaseSensitive setCaseSensitive = (Func_SetCaseSensitive)lib.Lib.GetProc("SetCaseSensitive"); + if (setCaseSensitive) + setCaseSensitive(CaseSensitive ? 1 : 0); + } + + lib.CreateObject = (Func_CreateObject)lib.Lib.GetProc("CreateObject"); + if (lib.CreateObject) + { + unsigned startSize = Codecs.Size() + Hashers.Size(); res = LoadCodecs(); - used = (Codecs.Size() != startSize); + used = (startSize != Codecs.Size() + Hashers.Size()); if (res == S_OK) { startSize = Formats.Size(); res = LoadFormats(); - used = used || (Formats.Size() != startSize); + if (startSize != Formats.Size()) + used = true; } } } + if (!used) Libs.DeleteBack(); + return res; } @@ -414,15 +550,31 @@ HRESULT CCodecs::LoadDllsFromFolder(const FString &folderPrefix) return S_OK; } -#endif - -#ifndef _SFX -static inline void SetBuffer(CByteBuffer &bb, const Byte *data, int size) +void CCodecs::CloseLibs() { - bb.SetCapacity(size); - memmove((Byte *)bb, data, size); + // OutputDebugStringA("~CloseLibs start"); + /* + WIN32: FreeLibrary() (CLibrary::Free()) function doesn't work as expected, + if it's called from another FreeLibrary() call. + So we need to call FreeLibrary() before global destructors. + + Also we free global links from DLLs to object of this module before CLibrary::Free() call. + */ + + FOR_VECTOR(i, Libs) + { + const CCodecLib &lib = Libs[i]; + if (lib.SetCodecs) + lib.SetCodecs(NULL); + } + + // OutputDebugStringA("~CloseLibs after SetCodecs"); + Libs.Clear(); + // OutputDebugStringA("~CloseLibs end"); } -#endif + +#endif // EXTERNAL_CODECS + HRESULT CCodecs::Load() { @@ -431,31 +583,92 @@ HRESULT CCodecs::Load() #endif Formats.Clear(); + #ifdef EXTERNAL_CODECS - Codecs.Clear(); + MainDll_ErrorPath.Empty(); + Codecs.Clear(); + Hashers.Clear(); #endif + for (UInt32 i = 0; i < g_NumArcs; i++) { const CArcInfo &arc = *g_Arcs[i]; CArcInfoEx item; - item.Name = arc.Name; + + item.Name.SetFromAscii(arc.Name); item.CreateInArchive = arc.CreateInArchive; - item.CreateOutArchive = arc.CreateOutArchive; - item.AddExts(arc.Ext, arc.AddExt); - item.UpdateEnabled = (arc.CreateOutArchive != 0); - item.KeepName = arc.KeepName; + item.IsArcFunc = arc.IsArc; + item.Flags = arc.Flags; + + { + UString e, ae; + if (arc.Ext) + e.SetFromAscii(arc.Ext); + if (arc.AddExt) + ae.SetFromAscii(arc.AddExt); + item.AddExts(e, ae); + } #ifndef _SFX - SetBuffer(item.StartSignature, arc.Signature, arc.SignatureSize); + + item.CreateOutArchive = arc.CreateOutArchive; + item.UpdateEnabled = (arc.CreateOutArchive != NULL); + item.SignatureOffset = arc.SignatureOffset; + // item.Version = MY_VER_MIX; + item.NewInterface = true; + + if (arc.IsMultiSignature()) + ParseSignatures(arc.Signature, arc.SignatureSize, item.Signatures); + else + item.Signatures.AddNew().CopyFrom(arc.Signature, arc.SignatureSize); + #endif + Formats.Add(item); } + #ifdef EXTERNAL_CODECS - const FString baseFolder = GetBaseFolderPrefixFromRegistry(); - RINOK(LoadDll(baseFolder + kMainDll, false)); - RINOK(LoadDllsFromFolder(baseFolder + kCodecsFolderName FSTRING_PATH_SEPARATOR)); - RINOK(LoadDllsFromFolder(baseFolder + kFormatsFolderName FSTRING_PATH_SEPARATOR)); + const FString baseFolder = GetBaseFolderPrefixFromRegistry(); + { + bool loadedOK; + RINOK(LoadDll(baseFolder + kMainDll, false, &loadedOK)); + if (!loadedOK) + MainDll_ErrorPath = kMainDll; + } + RINOK(LoadDllsFromFolder(baseFolder + kCodecsFolderName FSTRING_PATH_SEPARATOR)); + RINOK(LoadDllsFromFolder(baseFolder + kFormatsFolderName FSTRING_PATH_SEPARATOR)); + + NeedSetLibCodecs = true; + + if (Libs.Size() == 0) + NeedSetLibCodecs = false; + else if (Libs.Size() == 1) + { + // we don't need to set ISetCompressCodecsInfo, if all arcs and codecs are in one external module. + #ifndef EXPORT_CODECS + if (g_NumArcs == 0) + NeedSetLibCodecs = false; + #endif + } + + if (NeedSetLibCodecs) + { + /* 15.00: now we call global function in DLL: SetCompressCodecsInfo(c) + old versions called only ISetCompressCodecsInfo::SetCompressCodecsInfo(c) for each archive handler */ + + FOR_VECTOR(i, Libs) + { + CCodecLib &lib = Libs[i]; + lib.SetCodecs = (Func_SetCodecs)lib.Lib.GetProc("SetCodecs"); + if (lib.SetCodecs) + { + RINOK(lib.SetCodecs(this)); + } + } + } + #endif + return S_OK; } @@ -463,16 +676,21 @@ HRESULT CCodecs::Load() int CCodecs::FindFormatForArchiveName(const UString &arcPath) const { - int slashPos = arcPath.ReverseFind(WCHAR_PATH_SEPARATOR); - int dotPos = arcPath.ReverseFind(L'.'); - if (dotPos < 0 || dotPos < slashPos) + int dotPos = arcPath.ReverseFind_Dot(); + if (dotPos <= arcPath.ReverseFind_PathSepar()) return -1; - const UString ext = arcPath.Mid(dotPos + 1); - for (int i = 0; i < Formats.Size(); i++) + const UString ext = arcPath.Ptr(dotPos + 1); + if (ext.IsEmpty()) + return -1; + if (ext.IsEqualTo_Ascii_NoCase("exe")) + return -1; + FOR_VECTOR (i, Formats) { const CArcInfoEx &arc = Formats[i]; + /* if (!arc.UpdateEnabled) continue; + */ if (arc.FindExtension(ext) >= 0) return i; } @@ -483,7 +701,7 @@ int CCodecs::FindFormatForExtension(const UString &ext) const { if (ext.IsEmpty()) return -1; - for (int i = 0; i < Formats.Size(); i++) + FOR_VECTOR (i, Formats) if (Formats[i].FindExtension(ext) >= 0) return i; return -1; @@ -491,8 +709,8 @@ int CCodecs::FindFormatForExtension(const UString &ext) const int CCodecs::FindFormatForArchiveType(const UString &arcType) const { - for (int i = 0; i < Formats.Size(); i++) - if (Formats[i].Name.CompareNoCase(arcType) == 0) + FOR_VECTOR (i, Formats) + if (Formats[i].Name.IsEqualTo_NoCase(arcType)) return i; return -1; } @@ -500,12 +718,14 @@ int CCodecs::FindFormatForArchiveType(const UString &arcType) const bool CCodecs::FindFormatForArchiveType(const UString &arcType, CIntVector &formatIndices) const { formatIndices.Clear(); - for (int pos = 0; pos < arcType.Length();) + for (unsigned pos = 0; pos < arcType.Len();) { - int pos2 = arcType.Find('.', pos); + int pos2 = arcType.Find(L'.', pos); if (pos2 < 0) - pos2 = arcType.Length(); + pos2 = arcType.Len(); const UString name = arcType.Mid(pos, pos2 - pos); + if (name.IsEmpty()) + return false; int index = FindFormatForArchiveType(name); if (index < 0 && name != L"*") { @@ -518,24 +738,90 @@ bool CCodecs::FindFormatForArchiveType(const UString &arcType, CIntVector &forma return true; } -#endif +#endif // _SFX + + +#ifdef NEW_FOLDER_INTERFACE + +void CCodecIcons::LoadIcons(HMODULE m) +{ + UString iconTypes; + MyLoadString(m, kIconTypesResId, iconTypes); + UStringVector pairs; + SplitString(iconTypes, pairs); + FOR_VECTOR (i, pairs) + { + const UString &s = pairs[i]; + int pos = s.Find(L':'); + CIconPair iconPair; + iconPair.IconIndex = -1; + if (pos < 0) + pos = s.Len(); + else + { + UString num = s.Ptr(pos + 1); + if (!num.IsEmpty()) + { + const wchar_t *end; + iconPair.IconIndex = ConvertStringToUInt32(num, &end); + if (*end != 0) + continue; + } + } + iconPair.Ext = s.Left(pos); + IconPairs.Add(iconPair); + } +} + +bool CCodecIcons::FindIconIndex(const UString &ext, int &iconIndex) const +{ + iconIndex = -1; + FOR_VECTOR (i, IconPairs) + { + const CIconPair &pair = IconPairs[i]; + if (ext.IsEqualTo_NoCase(pair.Ext)) + { + iconIndex = pair.IconIndex; + return true; + } + } + return false; +} + +#endif // NEW_FOLDER_INTERFACE + #ifdef EXTERNAL_CODECS +// #define EXPORT_CODECS + #ifdef EXPORT_CODECS -extern unsigned int g_NumCodecs; -STDAPI CreateCoder2(bool encode, UInt32 index, const GUID *iid, void **outObject); + +extern unsigned g_NumCodecs; +STDAPI CreateDecoder(UInt32 index, const GUID *iid, void **outObject); +STDAPI CreateEncoder(UInt32 index, const GUID *iid, void **outObject); STDAPI GetMethodProperty(UInt32 codecIndex, PROPID propID, PROPVARIANT *value); -// STDAPI GetNumberOfMethods(UInt32 *numCodecs); -#endif +#define NUM_EXPORT_CODECS g_NumCodecs + +extern unsigned g_NumHashers; +STDAPI CreateHasher(UInt32 index, IHasher **hasher); +STDAPI GetHasherProp(UInt32 codecIndex, PROPID propID, PROPVARIANT *value); +#define NUM_EXPORT_HASHERS g_NumHashers + +#else // EXPORT_CODECS + +#define NUM_EXPORT_CODECS 0 +#define NUM_EXPORT_HASHERS 0 -STDMETHODIMP CCodecs::GetNumberOfMethods(UInt32 *numMethods) +#endif // EXPORT_CODECS + +STDMETHODIMP CCodecs::GetNumMethods(UInt32 *numMethods) { - *numMethods = - #ifdef EXPORT_CODECS - g_NumCodecs + - #endif - Codecs.Size(); + *numMethods = NUM_EXPORT_CODECS + #ifdef EXTERNAL_CODECS + + Codecs.Size() + #endif + ; return S_OK; } @@ -546,128 +832,197 @@ STDMETHODIMP CCodecs::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *valu return GetMethodProperty(index, propID, value); #endif - const CDllCodecInfo &ci = Codecs[index - #ifdef EXPORT_CODECS - - g_NumCodecs - #endif - ]; + #ifdef EXTERNAL_CODECS + const CDllCodecInfo &ci = Codecs[index - NUM_EXPORT_CODECS]; - if (propID == NMethodPropID::kDecoderIsAssigned) + if (propID == NMethodPropID::kDecoderIsAssigned || + propID == NMethodPropID::kEncoderIsAssigned) { - NWindows::NCOM::CPropVariant propVariant; - propVariant = ci.DecoderIsAssigned; - propVariant.Detach(value); - return S_OK; - } - if (propID == NMethodPropID::kEncoderIsAssigned) - { - NWindows::NCOM::CPropVariant propVariant; - propVariant = ci.EncoderIsAssigned; - propVariant.Detach(value); + NCOM::CPropVariant prop; + prop = (bool)((propID == NMethodPropID::kDecoderIsAssigned) ? + ci.DecoderIsAssigned : + ci.EncoderIsAssigned); + prop.Detach(value); return S_OK; } - return Libs[ci.LibIndex].GetMethodProperty(ci.CodecIndex, propID, value); + const CCodecLib &lib = Libs[ci.LibIndex]; + return lib.GetMethodProperty(ci.CodecIndex, propID, value); + #else + return E_FAIL; + #endif } STDMETHODIMP CCodecs::CreateDecoder(UInt32 index, const GUID *iid, void **coder) { #ifdef EXPORT_CODECS if (index < g_NumCodecs) - return CreateCoder2(false, index, iid, coder); + return CreateDecoder(index, iid, coder); #endif - const CDllCodecInfo &ci = Codecs[index - #ifdef EXPORT_CODECS - - g_NumCodecs - #endif - ]; + + #ifdef EXTERNAL_CODECS + const CDllCodecInfo &ci = Codecs[index - NUM_EXPORT_CODECS]; if (ci.DecoderIsAssigned) - return Libs[ci.LibIndex].CreateObject(&ci.Decoder, iid, (void **)coder); + { + const CCodecLib &lib = Libs[ci.LibIndex]; + if (lib.CreateDecoder) + return lib.CreateDecoder(ci.CodecIndex, iid, (void **)coder); + return lib.CreateObject(&ci.Decoder, iid, (void **)coder); + } return S_OK; + #else + return E_FAIL; + #endif } STDMETHODIMP CCodecs::CreateEncoder(UInt32 index, const GUID *iid, void **coder) { #ifdef EXPORT_CODECS if (index < g_NumCodecs) - return CreateCoder2(true, index, iid, coder); + return CreateEncoder(index, iid, coder); #endif - const CDllCodecInfo &ci = Codecs[index - #ifdef EXPORT_CODECS - - g_NumCodecs - #endif - ]; + + #ifdef EXTERNAL_CODECS + const CDllCodecInfo &ci = Codecs[index - NUM_EXPORT_CODECS]; if (ci.EncoderIsAssigned) - return Libs[ci.LibIndex].CreateObject(&ci.Encoder, iid, (void **)coder); + { + const CCodecLib &lib = Libs[ci.LibIndex]; + if (lib.CreateEncoder) + return lib.CreateEncoder(ci.CodecIndex, iid, (void **)coder); + return lib.CreateObject(&ci.Encoder, iid, (void **)coder); + } return S_OK; + #else + return E_FAIL; + #endif } -HRESULT CCodecs::CreateCoder(const UString &name, bool encode, CMyComPtr<ICompressCoder> &coder) const + +STDMETHODIMP_(UInt32) CCodecs::GetNumHashers() { - for (int i = 0; i < Codecs.Size(); i++) - { - const CDllCodecInfo &codec = Codecs[i]; - if (encode && !codec.EncoderIsAssigned || !encode && !codec.DecoderIsAssigned) - continue; - const CCodecLib &lib = Libs[codec.LibIndex]; - UString res; - NWindows::NCOM::CPropVariant prop; - RINOK(lib.GetMethodProperty(codec.CodecIndex, NMethodPropID::kName, &prop)); - if (prop.vt == VT_BSTR) - res = prop.bstrVal; - else if (prop.vt != VT_EMPTY) - continue; - if (name.CompareNoCase(res) == 0) - return lib.CreateObject(encode ? &codec.Encoder : &codec.Decoder, &IID_ICompressCoder, (void **)&coder); - } - return CLASS_E_CLASSNOTAVAILABLE; + return NUM_EXPORT_HASHERS + #ifdef EXTERNAL_CODECS + + Hashers.Size() + #endif + ; +} + +STDMETHODIMP CCodecs::GetHasherProp(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + #ifdef EXPORT_CODECS + if (index < g_NumHashers) + return ::GetHasherProp(index, propID, value); + #endif + + #ifdef EXTERNAL_CODECS + const CDllHasherInfo &ci = Hashers[index - NUM_EXPORT_HASHERS]; + return Libs[ci.LibIndex].ComHashers->GetHasherProp(ci.HasherIndex, propID, value); + #else + return E_FAIL; + #endif } -int CCodecs::GetCodecLibIndex(UInt32 index) +STDMETHODIMP CCodecs::CreateHasher(UInt32 index, IHasher **hasher) +{ + #ifdef EXPORT_CODECS + if (index < g_NumHashers) + return CreateHasher(index, hasher); + #endif + #ifdef EXTERNAL_CODECS + const CDllHasherInfo &ci = Hashers[index - NUM_EXPORT_HASHERS]; + return Libs[ci.LibIndex].ComHashers->CreateHasher(ci.HasherIndex, hasher); + #else + return E_FAIL; + #endif +} + +int CCodecs::GetCodec_LibIndex(UInt32 index) const { #ifdef EXPORT_CODECS if (index < g_NumCodecs) return -1; #endif + #ifdef EXTERNAL_CODECS - const CDllCodecInfo &ci = Codecs[index - #ifdef EXPORT_CODECS - - g_NumCodecs - #endif - ]; + const CDllCodecInfo &ci = Codecs[index - NUM_EXPORT_CODECS]; return ci.LibIndex; #else return -1; #endif } -bool CCodecs::GetCodecEncoderIsAssigned(UInt32 index) +int CCodecs::GetHasherLibIndex(UInt32 index) +{ + #ifdef EXPORT_CODECS + if (index < g_NumHashers) + return -1; + #endif + + #ifdef EXTERNAL_CODECS + const CDllHasherInfo &ci = Hashers[index - NUM_EXPORT_HASHERS]; + return ci.LibIndex; + #else + return -1; + #endif +} + +bool CCodecs::GetCodec_DecoderIsAssigned(UInt32 index) const { #ifdef EXPORT_CODECS if (index < g_NumCodecs) { - NWindows::NCOM::CPropVariant prop; - if (GetProperty(index, NMethodPropID::kEncoder, &prop) == S_OK) - if (prop.vt != VT_EMPTY) - return true; + NCOM::CPropVariant prop; + if (GetProperty(index, NMethodPropID::kDecoderIsAssigned, &prop) == S_OK) + { + if (prop.vt == VT_BOOL) + return VARIANT_BOOLToBool(prop.boolVal); + } return false; } #endif + #ifdef EXTERNAL_CODECS - const CDllCodecInfo &ci = Codecs[index - #ifdef EXPORT_CODECS - - g_NumCodecs - #endif - ]; - return ci.EncoderIsAssigned; + return Codecs[index - NUM_EXPORT_CODECS].DecoderIsAssigned; #else return false; #endif } -HRESULT CCodecs::GetCodecId(UInt32 index, UInt64 &id) +bool CCodecs::GetCodec_EncoderIsAssigned(UInt32 index) const { - UString s; - NWindows::NCOM::CPropVariant prop; + #ifdef EXPORT_CODECS + if (index < g_NumCodecs) + { + NCOM::CPropVariant prop; + if (GetProperty(index, NMethodPropID::kEncoderIsAssigned, &prop) == S_OK) + { + if (prop.vt == VT_BOOL) + return VARIANT_BOOLToBool(prop.boolVal); + } + return false; + } + #endif + + #ifdef EXTERNAL_CODECS + return Codecs[index - NUM_EXPORT_CODECS].EncoderIsAssigned; + #else + return false; + #endif +} + +UInt32 CCodecs::GetCodec_NumStreams(UInt32 index) +{ + NCOM::CPropVariant prop; + RINOK(GetProperty(index, NMethodPropID::kPackStreams, &prop)); + if (prop.vt == VT_UI4) + return (UInt32)prop.ulVal; + if (prop.vt == VT_EMPTY) + return 1; + return 0; +} + +HRESULT CCodecs::GetCodec_Id(UInt32 index, UInt64 &id) +{ + NCOM::CPropVariant prop; RINOK(GetProperty(index, NMethodPropID::kID, &prop)); if (prop.vt != VT_UI8) return E_INVALIDARG; @@ -675,14 +1030,43 @@ HRESULT CCodecs::GetCodecId(UInt32 index, UInt64 &id) return S_OK; } -UString CCodecs::GetCodecName(UInt32 index) +AString CCodecs::GetCodec_Name(UInt32 index) { - UString s; - NWindows::NCOM::CPropVariant prop; + AString s; + NCOM::CPropVariant prop; if (GetProperty(index, NMethodPropID::kName, &prop) == S_OK) if (prop.vt == VT_BSTR) - s = prop.bstrVal; + s.SetFromWStr_if_Ascii(prop.bstrVal); return s; } -#endif +UInt64 CCodecs::GetHasherId(UInt32 index) +{ + NCOM::CPropVariant prop; + if (GetHasherProp(index, NMethodPropID::kID, &prop) != S_OK) + return 0; + if (prop.vt != VT_UI8) + return 0; + return prop.uhVal.QuadPart; +} + +AString CCodecs::GetHasherName(UInt32 index) +{ + AString s; + NCOM::CPropVariant prop; + if (GetHasherProp(index, NMethodPropID::kName, &prop) == S_OK) + if (prop.vt == VT_BSTR) + s.SetFromWStr_if_Ascii(prop.bstrVal); + return s; +} + +UInt32 CCodecs::GetHasherDigestSize(UInt32 index) +{ + NCOM::CPropVariant prop; + RINOK(GetHasherProp(index, NMethodPropID::kDigestSize, &prop)); + if (prop.vt != VT_UI4) + return 0; + return prop.ulVal; +} + +#endif // EXTERNAL_CODECS diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/LoadCodecs.h b/3rdparty/lzma/CPP/7zip/UI/Common/LoadCodecs.h index f5963ed60ee..ac9eeac71dd 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/LoadCodecs.h +++ b/3rdparty/lzma/CPP/7zip/UI/Common/LoadCodecs.h @@ -3,34 +3,88 @@ #ifndef __LOAD_CODECS_H #define __LOAD_CODECS_H +/* +Client application uses LoadCodecs.* to load plugins to +CCodecs object, that contains 3 lists of plugins: + 1) Formats - internal and external archive handlers + 2) Codecs - external codecs + 3) Hashers - external hashers + +EXTERNAL_CODECS +--------------- + + if EXTERNAL_CODECS is defined, then the code tries to load external + plugins from DLL files (shared libraries). + + There are two types of executables in 7-Zip: + + 1) Executable that uses external plugins must be compiled + with EXTERNAL_CODECS defined: + - 7z.exe, 7zG.exe, 7zFM.exe + + Note: EXTERNAL_CODECS is used also in CPP/7zip/Common/CreateCoder.h + that code is used in plugin module (7z.dll). + + 2) Standalone modules are compiled without EXTERNAL_CODECS: + - SFX modules: 7z.sfx, 7zCon.sfx + - standalone versions of console 7-Zip: 7za.exe, 7zr.exe + + if EXTERNAL_CODECS is defined, CCodecs class implements interfaces: + - ICompressCodecsInfo : for Codecs + - IHashers : for Hashers + + The client application can send CCodecs object to each plugin module. + And plugin module can use ICompressCodecsInfo or IHashers interface to access + another plugins. + + There are 2 ways to send (ICompressCodecsInfo * compressCodecsInfo) to plugin + 1) for old versions: + a) request ISetCompressCodecsInfo from created archive handler. + b) call ISetCompressCodecsInfo::SetCompressCodecsInfo(compressCodecsInfo) + 2) for new versions: + a) request "SetCodecs" function from DLL file + b) call SetCodecs(compressCodecsInfo) function from DLL file +*/ + +#include "../../../Common/MyBuffer.h" #include "../../../Common/MyCom.h" #include "../../../Common/MyString.h" -#include "../../../Common/Buffer.h" -#include "../../ICoder.h" +#include "../../../Common/ComTry.h" #ifdef EXTERNAL_CODECS #include "../../../Windows/DLL.h" #endif +#include "../../ICoder.h" + +#include "../../Archive/IArchive.h" + + +#ifdef EXTERNAL_CODECS + struct CDllCodecInfo { - CLSID Encoder; - CLSID Decoder; + unsigned LibIndex; + UInt32 CodecIndex; bool EncoderIsAssigned; bool DecoderIsAssigned; - int LibIndex; - UInt32 CodecIndex; + CLSID Encoder; + CLSID Decoder; }; -#include "../../Archive/IArchive.h" +struct CDllHasherInfo +{ + unsigned LibIndex; + UInt32 HasherIndex; +}; -typedef IInArchive * (*CreateInArchiveP)(); -typedef IOutArchive * (*CreateOutArchiveP)(); +#endif struct CArcExtInfo { UString Ext; UString AddExt; + CArcExtInfo() {} CArcExtInfo(const UString &ext): Ext(ext) {} CArcExtInfo(const UString &ext, const UString &addExt): Ext(ext), AddExt(addExt) {} @@ -39,24 +93,45 @@ struct CArcExtInfo struct CArcInfoEx { - #ifdef EXTERNAL_CODECS - int LibIndex; - UInt32 FormatIndex; - CLSID ClassID; - #endif - bool UpdateEnabled; - CreateInArchiveP CreateInArchive; - CreateOutArchiveP CreateOutArchive; + UInt32 Flags; + + Func_CreateInArchive CreateInArchive; + Func_IsArc IsArcFunc; + UString Name; CObjectVector<CArcExtInfo> Exts; + #ifndef _SFX - CByteBuffer StartSignature; - // CByteBuffer FinishSignature; - #ifdef NEW_FOLDER_INTERFACE - UStringVector AssociateExts; + Func_CreateOutArchive CreateOutArchive; + bool UpdateEnabled; + bool NewInterface; + // UInt32 Version; + UInt32 SignatureOffset; + CObjectVector<CByteBuffer> Signatures; + #ifdef NEW_FOLDER_INTERFACE + UStringVector AssociateExts; + #endif #endif + + #ifdef EXTERNAL_CODECS + int LibIndex; + UInt32 FormatIndex; + CLSID ClassID; #endif - bool KeepName; + + bool Flags_KeepName() const { return (Flags & NArcInfoFlags::kKeepName) != 0; } + bool Flags_FindSignature() const { return (Flags & NArcInfoFlags::kFindSignature) != 0; } + + bool Flags_AltStreams() const { return (Flags & NArcInfoFlags::kAltStreams) != 0; } + bool Flags_NtSecure() const { return (Flags & NArcInfoFlags::kNtSecure) != 0; } + bool Flags_SymLinks() const { return (Flags & NArcInfoFlags::kSymLinks) != 0; } + bool Flags_HardLinks() const { return (Flags & NArcInfoFlags::kHardLinks) != 0; } + + bool Flags_UseGlobalOffset() const { return (Flags & NArcInfoFlags::kUseGlobalOffset) != 0; } + bool Flags_StartOpen() const { return (Flags & NArcInfoFlags::kStartOpen) != 0; } + bool Flags_BackwardOpen() const { return (Flags & NArcInfoFlags::kBackwardOpen) != 0; } + bool Flags_PreArc() const { return (Flags & NArcInfoFlags::kPreArc) != 0; } + bool Flags_PureStartOpen() const { return (Flags & NArcInfoFlags::kPureStartOpen) != 0; } UString GetMainExt() const { @@ -80,26 +155,30 @@ struct CArcInfoEx } */ - void AddExts(const wchar_t* ext, const wchar_t* addExt); + void AddExts(const UString &ext, const UString &addExt); + + bool IsSplit() const { return StringsAreEqualNoCase_Ascii(Name, "Split"); } + // bool IsRar() const { return StringsAreEqualNoCase_Ascii(Name, "Rar"); } CArcInfoEx(): - #ifdef EXTERNAL_CODECS - LibIndex(-1), - #endif - UpdateEnabled(false), - CreateInArchive(0), CreateOutArchive(0), - KeepName(false) - #ifndef _SFX - #endif + Flags(0), + CreateInArchive(NULL), + IsArcFunc(NULL) + #ifndef _SFX + , CreateOutArchive(NULL) + , UpdateEnabled(false) + , NewInterface(false) + // , Version(0) + , SignatureOffset(0) + #endif + #ifdef EXTERNAL_CODECS + , LibIndex(-1) + #endif {} }; -#ifdef EXTERNAL_CODECS -typedef UInt32 (WINAPI *GetMethodPropertyFunc)(UInt32 index, PROPID propID, PROPVARIANT *value); -typedef UInt32 (WINAPI *CreateObjectFunc)(const GUID *clsID, const GUID *interfaceID, void **outObject); - - #ifdef NEW_FOLDER_INTERFACE + struct CCodecIcons { struct CIconPair @@ -108,57 +187,128 @@ struct CCodecIcons int IconIndex; }; CObjectVector<CIconPair> IconPairs; + void LoadIcons(HMODULE m); bool FindIconIndex(const UString &ext, int &iconIndex) const; }; + #endif +#ifdef EXTERNAL_CODECS + struct CCodecLib -#ifdef NEW_FOLDER_INTERFACE -: public CCodecIcons -#endif + #ifdef NEW_FOLDER_INTERFACE + : public CCodecIcons + #endif { NWindows::NDLL::CLibrary Lib; - GetMethodPropertyFunc GetMethodProperty; - CreateObjectFunc CreateObject; - #ifdef NEW_FOLDER_INTERFACE FString Path; + + Func_CreateObject CreateObject; + Func_GetMethodProperty GetMethodProperty; + Func_CreateDecoder CreateDecoder; + Func_CreateEncoder CreateEncoder; + Func_SetCodecs SetCodecs; + + CMyComPtr<IHashers> ComHashers; + + #ifdef NEW_FOLDER_INTERFACE void LoadIcons() { CCodecIcons::LoadIcons((HMODULE)Lib); } #endif - CCodecLib(): GetMethodProperty(0) {} + + CCodecLib(): + CreateObject(NULL), + GetMethodProperty(NULL), + CreateDecoder(NULL), + CreateEncoder(NULL), + SetCodecs(NULL) + {} }; + #endif + class CCodecs: #ifdef EXTERNAL_CODECS - public ICompressCodecsInfo, + public ICompressCodecsInfo, + public IHashers, #else - public IUnknown, + public IUnknown, #endif public CMyUnknownImp { + CLASS_NO_COPY(CCodecs); public: #ifdef EXTERNAL_CODECS + CObjectVector<CCodecLib> Libs; - CObjectVector<CDllCodecInfo> Codecs; + FString MainDll_ErrorPath; - #ifdef NEW_FOLDER_INTERFACE - CCodecIcons InternalIcons; - #endif + void CloseLibs(); + + class CReleaser + { + CLASS_NO_COPY(CReleaser); + + /* CCodecsReleaser object releases CCodecs links. + 1) CCodecs is COM object that is deleted when all links to that object will be released/ + 2) CCodecs::Libs[i] can hold (ICompressCodecsInfo *) link to CCodecs object itself. + To break that reference loop, we must close all CCodecs::Libs in CCodecsReleaser desttructor. */ + + CCodecs *_codecs; + + public: + CReleaser(): _codecs(NULL) {} + void Set(CCodecs *codecs) { _codecs = codecs; } + ~CReleaser() { if (_codecs) _codecs->CloseLibs(); } + }; + + bool NeedSetLibCodecs; // = false, if we don't need to set codecs for archive handler via ISetCompressCodecsInfo HRESULT LoadCodecs(); HRESULT LoadFormats(); - HRESULT LoadDll(const FString &path, bool needCheckDll); + HRESULT LoadDll(const FString &path, bool needCheckDll, bool *loadedOK = NULL); HRESULT LoadDllsFromFolder(const FString &folderPrefix); - HRESULT CreateArchiveHandler(const CArcInfoEx &ai, void **archive, bool outHandler) const + HRESULT CreateArchiveHandler(const CArcInfoEx &ai, bool outHandler, void **archive) const { return Libs[ai.LibIndex].CreateObject(&ai.ClassID, outHandler ? &IID_IOutArchive : &IID_IInArchive, (void **)archive); } + + #endif + + #ifdef NEW_FOLDER_INTERFACE + CCodecIcons InternalIcons; #endif -public: CObjectVector<CArcInfoEx> Formats; + + #ifdef EXTERNAL_CODECS + CRecordVector<CDllCodecInfo> Codecs; + CRecordVector<CDllHasherInfo> Hashers; + #endif + + bool CaseSensitiveChange; + bool CaseSensitive; + + CCodecs(): + #ifdef EXTERNAL_CODECS + NeedSetLibCodecs(true), + #endif + CaseSensitiveChange(false), + CaseSensitive(false) + {} + + ~CCodecs() + { + // OutputDebugStringA("~CCodecs"); + } + + const wchar_t *GetFormatNamePtr(int formatIndex) const + { + return formatIndex < 0 ? L"#" : (const wchar_t *)Formats[formatIndex].Name; + } + HRESULT Load(); #ifndef _SFX @@ -168,65 +318,107 @@ public: bool FindFormatForArchiveType(const UString &arcType, CIntVector &formatIndices) const; #endif + #ifdef EXTERNAL_CODECS + + MY_UNKNOWN_IMP2(ICompressCodecsInfo, IHashers) + + STDMETHOD(GetNumMethods)(UInt32 *numMethods); + STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value); + STDMETHOD(CreateDecoder)(UInt32 index, const GUID *iid, void **coder); + STDMETHOD(CreateEncoder)(UInt32 index, const GUID *iid, void **coder); + + STDMETHOD_(UInt32, GetNumHashers)(); + STDMETHOD(GetHasherProp)(UInt32 index, PROPID propID, PROPVARIANT *value); + STDMETHOD(CreateHasher)(UInt32 index, IHasher **hasher); + + #else + MY_UNKNOWN_IMP + #endif // EXTERNAL_CODECS + + #ifdef EXTERNAL_CODECS - STDMETHOD(GetNumberOfMethods)(UInt32 *numMethods); - STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value); - STDMETHOD(CreateDecoder)(UInt32 index, const GUID *interfaceID, void **coder); - STDMETHOD(CreateEncoder)(UInt32 index, const GUID *interfaceID, void **coder); - #endif - int GetCodecLibIndex(UInt32 index); - bool GetCodecEncoderIsAssigned(UInt32 index); - HRESULT GetCodecId(UInt32 index, UInt64 &id); - UString GetCodecName(UInt32 index); + int GetCodec_LibIndex(UInt32 index) const; + bool GetCodec_DecoderIsAssigned(UInt32 index) const; + bool GetCodec_EncoderIsAssigned(UInt32 index) const; + UInt32 GetCodec_NumStreams(UInt32 index); + HRESULT GetCodec_Id(UInt32 index, UInt64 &id); + AString GetCodec_Name(UInt32 index); + + int GetHasherLibIndex(UInt32 index); + UInt64 GetHasherId(UInt32 index); + AString GetHasherName(UInt32 index); + UInt32 GetHasherDigestSize(UInt32 index); + + #endif - HRESULT CreateInArchive(int formatIndex, CMyComPtr<IInArchive> &archive) const + HRESULT CreateInArchive(unsigned formatIndex, CMyComPtr<IInArchive> &archive) const { const CArcInfoEx &ai = Formats[formatIndex]; #ifdef EXTERNAL_CODECS if (ai.LibIndex < 0) #endif { + COM_TRY_BEGIN archive = ai.CreateInArchive(); return S_OK; + COM_TRY_END } #ifdef EXTERNAL_CODECS - return CreateArchiveHandler(ai, (void **)&archive, false); + return CreateArchiveHandler(ai, false, (void **)&archive); #endif } - HRESULT CreateOutArchive(int formatIndex, CMyComPtr<IOutArchive> &archive) const + + #ifndef _SFX + + HRESULT CreateOutArchive(unsigned formatIndex, CMyComPtr<IOutArchive> &archive) const { const CArcInfoEx &ai = Formats[formatIndex]; #ifdef EXTERNAL_CODECS if (ai.LibIndex < 0) #endif { + COM_TRY_BEGIN archive = ai.CreateOutArchive(); return S_OK; + COM_TRY_END } + #ifdef EXTERNAL_CODECS - return CreateArchiveHandler(ai, (void **)&archive, true); + return CreateArchiveHandler(ai, true, (void **)&archive); #endif } + int FindOutFormatFromName(const UString &name) const { - for (int i = 0; i < Formats.Size(); i++) + FOR_VECTOR (i, Formats) { const CArcInfoEx &arc = Formats[i]; if (!arc.UpdateEnabled) continue; - if (arc.Name.CompareNoCase(name) == 0) + if (arc.Name.IsEqualTo_NoCase(name)) return i; } return -1; } - #ifdef EXTERNAL_CODECS - HRESULT CreateCoder(const UString &name, bool encode, CMyComPtr<ICompressCoder> &coder) const; - #endif - + #endif // _SFX }; +#ifdef EXTERNAL_CODECS + #define CREATE_CODECS_OBJECT \ + CCodecs *codecs = new CCodecs; \ + CExternalCodecs __externalCodecs; \ + __externalCodecs.GetCodecs = codecs; \ + __externalCodecs.GetHashers = codecs; \ + CCodecs::CReleaser codecsReleaser; \ + codecsReleaser.Set(codecs); +#else + #define CREATE_CODECS_OBJECT \ + CCodecs *codecs = new CCodecs; \ + CMyComPtr<IUnknown> __codecsRef = codecs; +#endif + #endif diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/OpenArchive.cpp b/3rdparty/lzma/CPP/7zip/UI/Common/OpenArchive.cpp index 60d2f6f5ad1..e323abd2197 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/OpenArchive.cpp +++ b/3rdparty/lzma/CPP/7zip/UI/Common/OpenArchive.cpp @@ -2,27 +2,454 @@ #include "StdAfx.h" -#include "Common/Wildcard.h" +// #define SHOW_DEBUG_INFO -#include "Windows/FileDir.h" -#include "Windows/PropVariant.h" +#ifdef SHOW_DEBUG_INFO +#include <stdio.h> +#endif + +#include "../../../../C/CpuArch.h" + +#include "../../../Common/ComTry.h" +#include "../../../Common/IntToString.h" +#include "../../../Common/StringConvert.h" +#include "../../../Common/StringToInt.h" +#include "../../../Common/Wildcard.h" + +#include "../../../Windows/FileDir.h" #include "../../Common/FileStreams.h" +#include "../../Common/LimitedStreams.h" +#include "../../Common/ProgressUtils.h" #include "../../Common/StreamUtils.h" +#include "../../Compress/CopyCoder.h" + #include "DefaultName.h" #include "OpenArchive.h" +#ifndef _SFX +#include "SetProperties.h" +#endif + +#ifdef SHOW_DEBUG_INFO +#define PRF(x) x +#else +#define PRF(x) +#endif + +// increase it, if you need to support larger SFX stubs +static const UInt64 kMaxCheckStartPosition = 1 << 23; + +/* +Open: + - formatIndex >= 0 (exact Format) + 1) Open with main type. Archive handler is allowed to use archive start finder. + Warning, if there is tail. + + - formatIndex = -1 (Parser:0) (default) + - same as #1 but doesn't return Parser + + - formatIndex = -2 (#1) + - file has supported extension (like a.7z) + Open with that main type (only starting from start of file). + - open OK: + - if there is no tail - return OK + - if there is tail: + - archive is not "Self Exe" - return OK with Warning, that there is tail + - archive is "Self Exe" + ignore "Self Exe" stub, and tries to open tail + - tail can be open as archive - shows that archive and stub size property. + - tail can't be open as archive - shows Parser ??? + - open FAIL: + Try to open with all other types from offset 0 only. + If some open type is OK and physical archive size is uequal or larger + than file size, then return that archive with warning that can not be open as [extension type]. + If extension was EXE, it will try to open as unknown_extension case + - file has unknown extension (like a.hhh) + It tries to open via parser code. + - if there is full archive or tail archive and unknown block or "Self Exe" + at front, it shows tail archive and stub size property. + - in another cases, if there is some archive inside file, it returns parser/ + - in another cases, it retuens S_FALSE + + + - formatIndex = -3 (#2) + - same as #1, but + - stub (EXE) + archive is open in Parser + + - formatIndex = -4 (#3) + - returns only Parser. skip full file archive. And show other sub-archives + + - formatIndex = -5 (#4) + - returns only Parser. skip full file archive. And show other sub-archives for each byte pos + +*/ + + + + using namespace NWindows; -// Static-SFX (for Linux) can be big. -const UInt64 kMaxCheckStartPosition = 1 << 22; +/* +#ifdef _SFX +#define OPEN_PROPS_PARAM +#else +#define OPEN_PROPS_PARAM , props +#endif +*/ + +/* +CArc::~CArc() +{ + GetRawProps.Release(); + Archive.Release(); + printf("\nCArc::~CArc()\n"); +} +*/ + +#ifndef _SFX + +namespace NArchive { +namespace NParser { + +struct CParseItem +{ + UInt64 Offset; + UInt64 Size; + // UInt64 OkSize; + UString Name; + UString Extension; + FILETIME FileTime; + UString Comment; + UString ArcType; + + bool FileTime_Defined; + bool UnpackSize_Defined; + bool NumSubDirs_Defined; + bool NumSubFiles_Defined; + + bool IsSelfExe; + bool IsNotArcType; + + UInt64 UnpackSize; + UInt64 NumSubDirs; + UInt64 NumSubFiles; + + int FormatIndex; + + bool LenIsUnknown; + + CParseItem(): + LenIsUnknown(false), + FileTime_Defined(false), + UnpackSize_Defined(false), + NumSubFiles_Defined(false), + NumSubDirs_Defined(false), + IsSelfExe(false), + IsNotArcType(false) + // OkSize(0) + {} + + /* + bool IsEqualTo(const CParseItem &item) const + { + return Offset == item.Offset && Size == item.Size; + } + */ + + void NormalizeOffset() + { + if ((Int64)Offset < 0) + { + Size += Offset; + // OkSize += Offset; + Offset = 0; + } + } +}; + +class CHandler: + public IInArchive, + public IInArchiveGetStream, + public CMyUnknownImp +{ +public: + CObjectVector<CParseItem> _items; + UInt64 _maxEndOffset; + CMyComPtr<IInStream> _stream; + + MY_UNKNOWN_IMP2( + IInArchive, + IInArchiveGetStream) + + INTERFACE_IInArchive(;) + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); + + UInt64 GetLastEnd() const + { + if (_items.IsEmpty()) + return 0; + const CParseItem &back = _items.Back(); + return back.Offset + back.Size; + } + + void AddUnknownItem(UInt64 next); + int FindInsertPos(const CParseItem &item) const; + void AddItem(const CParseItem &item); + + CHandler(): _maxEndOffset(0) {} +}; + +int CHandler::FindInsertPos(const CParseItem &item) const +{ + unsigned left = 0, right = _items.Size(); + while (left != right) + { + unsigned mid = (left + right) / 2; + const CParseItem & midItem = _items[mid]; + if (item.Offset < midItem.Offset) + right = mid; + else if (item.Offset > midItem.Offset) + left = mid + 1; + else if (item.Size < midItem.Size) + right = mid; + else if (item.Size > midItem.Size) + left = mid + 1; + else + { + left = mid + 1; + // return -1; + } + } + return left; +} + +void CHandler::AddUnknownItem(UInt64 next) +{ + /* + UInt64 prevEnd = 0; + if (!_items.IsEmpty()) + { + const CParseItem &back = _items.Back(); + prevEnd = back.Offset + back.Size; + } + */ + if (_maxEndOffset < next) + { + CParseItem item2; + item2.Offset = _maxEndOffset; + item2.Size = next - _maxEndOffset; + _maxEndOffset = next; + _items.Add(item2); + } + else if (_maxEndOffset > next && !_items.IsEmpty()) + { + CParseItem &back = _items.Back(); + if (back.LenIsUnknown) + { + back.Size = next - back.Offset; + _maxEndOffset = next; + } + } +} + +void CHandler::AddItem(const CParseItem &item) +{ + AddUnknownItem(item.Offset); + int pos = FindInsertPos(item); + if (pos >= 0) + { + _items.Insert(pos, item); + UInt64 next = item.Offset + item.Size; + if (_maxEndOffset < next) + _maxEndOffset = next; + } +} + +/* +static const CStatProp kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidMTime, VT_FILETIME}, + { NULL, kpidType, VT_BSTR}, + { NULL, kpidComment, VT_BSTR}, + { NULL, kpidOffset, VT_UI8}, + { NULL, kpidUnpackSize, VT_UI8}, +// { NULL, kpidNumSubDirs, VT_UI8}, +}; +*/ + +static const Byte kProps[] = +{ + kpidPath, + kpidSize, + kpidMTime, + kpidType, + kpidComment, + kpidOffset, + kpidUnpackSize +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps_NO + +STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback * /* openArchiveCallback */) +{ + COM_TRY_BEGIN + { + Close(); + _stream = stream; + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _items.Clear(); + _stream.Release(); + return S_OK; +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _items.Size(); + return S_OK; +} -HRESULT GetArchiveItemBoolProp(IInArchive *archive, UInt32 index, PROPID propID, bool &result) +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NCOM::CPropVariant prop; + + const CParseItem &item = _items[index]; + + switch (propID) + { + case kpidPath: + { + wchar_t sz[32]; + ConvertUInt32ToString(index + 1, sz); + UString s = sz; + if (!item.Name.IsEmpty()) + { + s += L'.'; + s += item.Name; + } + if (!item.Extension.IsEmpty()) + { + s += L'.'; + s += item.Extension; + } + prop = s; break; + } + case kpidSize: + case kpidPackSize: prop = item.Size; break; + case kpidOffset: prop = item.Offset; break; + case kpidUnpackSize: if (item.UnpackSize_Defined) prop = item.UnpackSize; break; + case kpidNumSubFiles: if (item.NumSubFiles_Defined) prop = item.NumSubFiles; break; + case kpidNumSubDirs: if (item.NumSubDirs_Defined) prop = item.NumSubDirs; break; + case kpidMTime: if (item.FileTime_Defined) prop = item.FileTime; break; + case kpidComment: if (!item.Comment.IsEmpty()) prop = item.Comment; break; + case kpidType: if (!item.ArcType.IsEmpty()) prop = item.ArcType; break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + + bool allFilesMode = (numItems == (UInt32)(Int32)-1); + if (allFilesMode) + numItems = _items.Size(); + if (_stream && numItems == 0) + return S_OK; + UInt64 totalSize = 0; + UInt32 i; + for (i = 0; i < numItems; i++) + totalSize += _items[allFilesMode ? i : indices[i]].Size; + extractCallback->SetTotal(totalSize); + + totalSize = 0; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> inStream(streamSpec); + streamSpec->SetStream(_stream); + + CLimitedSequentialOutStream *outStreamSpec = new CLimitedSequentialOutStream; + CMyComPtr<ISequentialOutStream> outStream(outStreamSpec); + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + for (i = 0; i < numItems; i++) + { + lps->InSize = totalSize; + lps->OutSize = totalSize; + RINOK(lps->SetCur()); + CMyComPtr<ISequentialOutStream> realOutStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + Int32 index = allFilesMode ? i : indices[i]; + const CParseItem &item = _items[index]; + + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + UInt64 unpackSize = item.Size; + totalSize += unpackSize; + bool skipMode = false; + if (!testMode && !realOutStream) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + + outStreamSpec->SetStream(realOutStream); + realOutStream.Release(); + outStreamSpec->Init(skipMode ? 0 : unpackSize, true); + + Int32 opRes = NExtract::NOperationResult::kOK; + RINOK(_stream->Seek(item.Offset, STREAM_SEEK_SET, NULL)); + streamSpec->Init(unpackSize); + RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)); + + if (outStreamSpec->GetRem() != 0) + opRes = NExtract::NOperationResult::kDataError; + outStreamSpec->ReleaseStream(); + RINOK(extractCallback->SetOperationResult(opRes)); + } + + return S_OK; + + COM_TRY_END +} + + +STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) +{ + COM_TRY_BEGIN + const CParseItem &item = _items[index]; + return CreateLimitedInStream(_stream, item.Offset, item.Size, stream); + COM_TRY_END +} + +}} + +#endif + +HRESULT Archive_GetItemBoolProp(IInArchive *arc, UInt32 index, PROPID propID, bool &result) throw() { NCOM::CPropVariant prop; result = false; - RINOK(archive->GetProperty(index, propID, &prop)); + RINOK(arc->GetProperty(index, propID, &prop)); if (prop.vt == VT_BOOL) result = VARIANT_BOOLToBool(prop.boolVal); else if (prop.vt != VT_EMPTY) @@ -30,24 +457,276 @@ HRESULT GetArchiveItemBoolProp(IInArchive *archive, UInt32 index, PROPID propID, return S_OK; } -HRESULT IsArchiveItemFolder(IInArchive *archive, UInt32 index, bool &result) +HRESULT Archive_IsItem_Dir(IInArchive *arc, UInt32 index, bool &result) throw() { - return GetArchiveItemBoolProp(archive, index, kpidIsDir, result); + return Archive_GetItemBoolProp(arc, index, kpidIsDir, result); } +HRESULT Archive_IsItem_Aux(IInArchive *arc, UInt32 index, bool &result) throw() +{ + return Archive_GetItemBoolProp(arc, index, kpidIsAux, result); +} + +HRESULT Archive_IsItem_AltStream(IInArchive *arc, UInt32 index, bool &result) throw() +{ + return Archive_GetItemBoolProp(arc, index, kpidIsAltStream, result); +} + +HRESULT Archive_IsItem_Deleted(IInArchive *arc, UInt32 index, bool &result) throw() +{ + return Archive_GetItemBoolProp(arc, index, kpidIsDeleted, result); +} + +static HRESULT Archive_GetArcBoolProp(IInArchive *arc, PROPID propid, bool &result) throw() +{ + NCOM::CPropVariant prop; + result = false; + RINOK(arc->GetArchiveProperty(propid, &prop)); + if (prop.vt == VT_BOOL) + result = VARIANT_BOOLToBool(prop.boolVal); + else if (prop.vt != VT_EMPTY) + return E_FAIL; + return S_OK; +} + +static HRESULT Archive_GetArcProp_UInt(IInArchive *arc, PROPID propid, UInt64 &result, bool &defined) +{ + defined = false; + NCOM::CPropVariant prop; + RINOK(arc->GetArchiveProperty(propid, &prop)); + switch (prop.vt) + { + case VT_UI4: result = prop.ulVal; defined = true; break; + case VT_I4: result = prop.lVal; defined = true; break; + case VT_UI8: result = (UInt64)prop.uhVal.QuadPart; defined = true; break; + case VT_I8: result = (UInt64)prop.hVal.QuadPart; defined = true; break; + case VT_EMPTY: break; + default: return E_FAIL; + } + return S_OK; +} + +static HRESULT Archive_GetArcProp_Int(IInArchive *arc, PROPID propid, Int64 &result, bool &defined) +{ + defined = false; + NCOM::CPropVariant prop; + RINOK(arc->GetArchiveProperty(propid, &prop)); + switch (prop.vt) + { + case VT_UI4: result = prop.ulVal; defined = true; break; + case VT_I4: result = prop.lVal; defined = true; break; + case VT_UI8: result = (Int64)prop.uhVal.QuadPart; defined = true; break; + case VT_I8: result = (Int64)prop.hVal.QuadPart; defined = true; break; + case VT_EMPTY: break; + default: return E_FAIL; + } + return S_OK; +} + +#ifndef _SFX + +HRESULT CArc::GetItemPathToParent(UInt32 index, UInt32 parent, UStringVector &parts) const +{ + if (!GetRawProps) + return E_FAIL; + if (index == parent) + return S_OK; + UInt32 curIndex = index; + + UString s; + + bool prevWasAltStream = false; + + for (;;) + { + #ifdef MY_CPU_LE + const void *p; + UInt32 size; + UInt32 propType; + RINOK(GetRawProps->GetRawProp(curIndex, kpidName, &p, &size, &propType)); + if (p && propType == PROP_DATA_TYPE_wchar_t_PTR_Z_LE) + s = (const wchar_t *)p; + else + #endif + { + NCOM::CPropVariant prop; + RINOK(Archive->GetProperty(curIndex, kpidName, &prop)); + if (prop.vt == VT_BSTR && prop.bstrVal) + s.SetFromBstr(prop.bstrVal); + else if (prop.vt == VT_EMPTY) + s.Empty(); + else + return E_FAIL; + } + + UInt32 curParent = (UInt32)(Int32)-1; + UInt32 parentType = 0; + RINOK(GetRawProps->GetParent(curIndex, &curParent, &parentType)); + + if (parentType != NParentType::kAltStream) + { + for (;;) + { + int pos = s.ReverseFind_PathSepar(); + if (pos < 0) + { + break; + } + parts.Insert(0, s.Ptr(pos + 1)); + s.DeleteFrom(pos); + } + } + + parts.Insert(0, s); + + if (prevWasAltStream) + { + { + UString &s = parts[parts.Size() - 2]; + s += L':'; + s += parts.Back(); + } + parts.DeleteBack(); + } + + if (parent == curParent) + return S_OK; + + prevWasAltStream = false; + if (parentType == NParentType::kAltStream) + prevWasAltStream = true; + + if (curParent == (UInt32)(Int32)-1) + return E_FAIL; + curIndex = curParent; + } +} + +#endif + HRESULT CArc::GetItemPath(UInt32 index, UString &result) const { + #ifdef MY_CPU_LE + if (GetRawProps) + { + const void *p; + UInt32 size; + UInt32 propType; + if (!IsTree) + { + if (GetRawProps->GetRawProp(index, kpidPath, &p, &size, &propType) == S_OK && + propType == NPropDataType::kUtf16z) + { + unsigned len = size / 2 - 1; + wchar_t *s = result.GetBuf(len); + for (unsigned i = 0; i < len; i++) + { + wchar_t c = GetUi16(p); + p = (const void *)((const Byte *)p + 2); + #if WCHAR_PATH_SEPARATOR != L'/' + if (c == L'/') + c = WCHAR_PATH_SEPARATOR; + #endif + *s++ = c; + } + *s = 0; + result.ReleaseBuf_SetLen(len); + if (len != 0) + return S_OK; + } + } + /* + else if (GetRawProps->GetRawProp(index, kpidName, &p, &size, &propType) == S_OK && + p && propType == NPropDataType::kUtf16z) + { + size -= 2; + UInt32 totalSize = size; + bool isOK = false; + + { + UInt32 index2 = index; + for (;;) + { + UInt32 parent = (UInt32)(Int32)-1; + UInt32 parentType = 0; + if (GetRawProps->GetParent(index2, &parent, &parentType) != S_OK) + break; + if (parent == (UInt32)(Int32)-1) + { + if (parentType != 0) + totalSize += 2; + isOK = true; + break; + } + index2 = parent; + UInt32 size2; + const void *p2; + if (GetRawProps->GetRawProp(index2, kpidName, &p2, &size2, &propType) != S_OK && + p2 && propType == NPropDataType::kUtf16z) + break; + totalSize += size2; + } + } + + if (isOK) + { + wchar_t *sz = result.GetBuf_SetEnd(totalSize / 2); + UInt32 pos = totalSize - size; + memcpy((Byte *)sz + pos, p, size); + UInt32 index2 = index; + for (;;) + { + UInt32 parent = (UInt32)(Int32)-1; + UInt32 parentType = 0; + if (GetRawProps->GetParent(index2, &parent, &parentType) != S_OK) + break; + if (parent == (UInt32)(Int32)-1) + { + if (parentType != 0) + sz[pos / 2 - 1] = L':'; + break; + } + index2 = parent; + UInt32 size2; + const void *p2; + if (GetRawProps->GetRawProp(index2, kpidName, &p2, &size2, &propType) != S_OK) + break; + pos -= size2; + memcpy((Byte *)sz + pos, p2, size2); + sz[(pos + size2 - 2) / 2] = (parentType == 0) ? WCHAR_PATH_SEPARATOR : L':'; + } + #ifdef _WIN32 + // result.Replace(L'/', WCHAR_PATH_SEPARATOR); + #endif + return S_OK; + } + } + */ + } + #endif + { NCOM::CPropVariant prop; RINOK(Archive->GetProperty(index, kpidPath, &prop)); - if (prop.vt == VT_BSTR) - result = prop.bstrVal; + if (prop.vt == VT_BSTR && prop.bstrVal) + result.SetFromBstr(prop.bstrVal); else if (prop.vt == VT_EMPTY) result.Empty(); else return E_FAIL; } + if (result.IsEmpty()) + return GetDefaultItemPath(index, result); + return S_OK; +} + +HRESULT CArc::GetDefaultItemPath(UInt32 index, UString &result) const +{ + result.Empty(); + bool isDir; + RINOK(Archive_IsItem_Dir(Archive, index, isDir)); + if (!isDir) { result = DefaultName; NCOM::CPropVariant prop; @@ -63,6 +742,197 @@ HRESULT CArc::GetItemPath(UInt32 index, UString &result) const return S_OK; } +HRESULT CArc::GetItemPath2(UInt32 index, UString &result) const +{ + RINOK(GetItemPath(index, result)); + if (Ask_Deleted) + { + bool isDeleted = false; + RINOK(Archive_IsItem_Deleted(Archive, index, isDeleted)); + if (isDeleted) + result.Insert(0, L"[DELETED]" WSTRING_PATH_SEPARATOR); + } + return S_OK; +} + +#ifdef SUPPORT_ALT_STREAMS + +int FindAltStreamColon_in_Path(const wchar_t *path) +{ + unsigned i = 0; + int colonPos = -1; + for (;; i++) + { + wchar_t c = path[i]; + if (c == 0) + return colonPos; + if (c == ':') + { + if (colonPos < 0) + colonPos = i; + continue; + } + if (c == WCHAR_PATH_SEPARATOR) + colonPos = -1; + } +} + +#endif + +HRESULT CArc::GetItem(UInt32 index, CReadArcItem &item) const +{ + #ifdef SUPPORT_ALT_STREAMS + item.IsAltStream = false; + item.AltStreamName.Empty(); + item.MainPath.Empty(); + #endif + + item.IsDir = false; + item.Path.Empty(); + item.ParentIndex = (UInt32)(Int32)-1; + + item.PathParts.Clear(); + + RINOK(Archive_IsItem_Dir(Archive, index, item.IsDir)); + item.MainIsDir = item.IsDir; + + RINOK(GetItemPath2(index, item.Path)); + + #ifndef _SFX + UInt32 mainIndex = index; + #endif + + #ifdef SUPPORT_ALT_STREAMS + + item.MainPath = item.Path; + if (Ask_AltStream) + { + RINOK(Archive_IsItem_AltStream(Archive, index, item.IsAltStream)); + } + + bool needFindAltStream = false; + + if (item.IsAltStream) + { + needFindAltStream = true; + if (GetRawProps) + { + UInt32 parentType = 0; + UInt32 parentIndex; + RINOK(GetRawProps->GetParent(index, &parentIndex, &parentType)); + if (parentType == NParentType::kAltStream) + { + NCOM::CPropVariant prop; + RINOK(Archive->GetProperty(index, kpidName, &prop)); + if (prop.vt == VT_BSTR && prop.bstrVal) + item.AltStreamName.SetFromBstr(prop.bstrVal); + else if (prop.vt != VT_EMPTY) + return E_FAIL; + else + { + // item.IsAltStream = false; + } + /* + if (item.AltStreamName.IsEmpty()) + item.IsAltStream = false; + */ + + needFindAltStream = false; + item.ParentIndex = parentIndex; + mainIndex = parentIndex; + + if (parentIndex == (UInt32)(Int32)-1) + { + item.MainPath.Empty(); + item.MainIsDir = true; + } + else + { + RINOK(GetItemPath2(parentIndex, item.MainPath)); + RINOK(Archive_IsItem_Dir(Archive, parentIndex, item.MainIsDir)); + } + } + } + } + + if (item.WriteToAltStreamIfColon || needFindAltStream) + { + /* Good handler must support GetRawProps::GetParent for alt streams./ + So the following code currently is not used */ + int colon = FindAltStreamColon_in_Path(item.Path); + if (colon >= 0) + { + item.MainPath.DeleteFrom(colon); + item.AltStreamName = item.Path.Ptr(colon + 1); + item.MainIsDir = (colon == 0 || IsPathSepar(item.Path[colon - 1])); + item.IsAltStream = true; + } + } + + #endif + + #ifndef _SFX + if (item._use_baseParentFolder_mode) + { + RINOK(GetItemPathToParent(mainIndex, item._baseParentFolder, item.PathParts)); + + #ifdef SUPPORT_ALT_STREAMS + if ((item.WriteToAltStreamIfColon || needFindAltStream) && !item.PathParts.IsEmpty()) + { + int colon; + { + UString &s = item.PathParts.Back(); + colon = FindAltStreamColon_in_Path(s); + if (colon >= 0) + { + item.AltStreamName = s.Ptr(colon + 1); + item.MainIsDir = (colon == 0 || IsPathSepar(s[(unsigned)colon - 1])); + item.IsAltStream = true; + s.DeleteFrom(colon); + } + } + if (colon == 0) + item.PathParts.DeleteBack(); + } + #endif + + } + else + #endif + SplitPathToParts( + #ifdef SUPPORT_ALT_STREAMS + item.MainPath + #else + item.Path + #endif + , item.PathParts); + + return S_OK; +} + +#ifndef _SFX + +static HRESULT Archive_GetItem_Size(IInArchive *archive, UInt32 index, UInt64 &size, bool &defined) +{ + NCOM::CPropVariant prop; + defined = false; + size = 0; + RINOK(archive->GetProperty(index, kpidSize, &prop)); + switch (prop.vt) + { + case VT_UI1: size = prop.bVal; break; + case VT_UI2: size = prop.uiVal; break; + case VT_UI4: size = prop.ulVal; break; + case VT_UI8: size = (UInt64)prop.uhVal.QuadPart; break; + case VT_EMPTY: return S_OK; + default: return E_FAIL; + } + defined = true; + return S_OK; +} + +#endif + HRESULT CArc::GetItemSize(UInt32 index, UInt64 &size, bool &defined) const { NCOM::CPropVariant prop; @@ -104,6 +974,7 @@ HRESULT CArc::GetItemMTime(UInt32 index, FILETIME &ft, bool &defined) const } #ifndef _SFX + static inline bool TestSignature(const Byte *p1, const Byte *p2, size_t size) { for (size_t i = 0; i < size; i++) @@ -111,296 +982,2097 @@ static inline bool TestSignature(const Byte *p1, const Byte *p2, size_t size) return false; return true; } + +static void MakeCheckOrder(CCodecs *codecs, + CIntVector &orderIndices, unsigned numTypes, CIntVector &orderIndices2, + const Byte *data, size_t dataSize) +{ + for (unsigned i = 0; i < numTypes; i++) + { + int index = orderIndices[i]; + if (index < 0) + continue; + const CArcInfoEx &ai = codecs->Formats[index]; + if (ai.SignatureOffset != 0) + { + orderIndices2.Add(index); + orderIndices[i] = -1; + continue; + } + + const CObjectVector<CByteBuffer> &sigs = ai.Signatures; + FOR_VECTOR (k, sigs) + { + const CByteBuffer &sig = sigs[k]; + if (sig.Size() == 0 && dataSize == 0 || + sig.Size() != 0 && sig.Size() <= dataSize && + TestSignature(data, sig, sig.Size())) + { + orderIndices2.Add(index); + orderIndices[i] = -1; + break; + } + } + } +} + #endif #ifdef UNDER_CE -static const int kNumHashBytes = 1; -#define HASH_VAL(buf, pos) ((buf)[pos]) + static const unsigned kNumHashBytes = 1; + #define HASH_VAL(buf, pos) ((buf)[pos]) #else -static const int kNumHashBytes = 2; -#define HASH_VAL(buf, pos) ((buf)[pos] | ((UInt32)(buf)[pos + 1] << 8)) + static const unsigned kNumHashBytes = 2; + #define HASH_VAL(buf, pos) ((buf)[pos] | ((UInt32)(buf)[pos + 1] << 8)) #endif -HRESULT CArc::OpenStream( - CCodecs *codecs, - int formatIndex, - IInStream *stream, - ISequentialInStream *seqStream, - IArchiveOpenCallback *callback) +#ifndef _SFX + +static bool IsExeExt(const UString &ext) { - Archive.Release(); + return ext.IsEqualTo_Ascii_NoCase("exe"); +} + +static const char * const k_PreArcFormats[] = +{ + "pe" + , "elf" + , "macho" + , "mub" + , "te" +}; + +static bool IsNameFromList(const UString &s, const char * const names[], size_t num) +{ + for (unsigned i = 0; i < num; i++) + if (StringsAreEqualNoCase_Ascii(s, names[i])) + return true; + return false; +} + + +static bool IsPreArcFormat(const CArcInfoEx &ai) +{ + if (ai.Flags_PreArc()) + return true; + return IsNameFromList(ai.Name, k_PreArcFormats, ARRAY_SIZE(k_PreArcFormats)); +} + +static const char * const k_Formats_with_simple_signuature[] = +{ + "7z" + , "xz" + , "rar" + , "bzip2" + , "gzip" + , "cab" + , "wim" + , "rpm" + , "vhd" + , "xar" +}; + +static bool IsNewStyleSignature(const CArcInfoEx &ai) +{ + // if (ai.Version >= 0x91F) + if (ai.NewInterface) + return true; + return IsNameFromList(ai.Name, k_Formats_with_simple_signuature, ARRAY_SIZE(k_Formats_with_simple_signuature)); +} + +class CArchiveOpenCallback_Offset: + public IArchiveOpenCallback, + public IArchiveOpenVolumeCallback, + #ifndef _NO_CRYPTO + public ICryptoGetTextPassword, + #endif + public CMyUnknownImp +{ +public: + CMyComPtr<IArchiveOpenCallback> Callback; + CMyComPtr<IArchiveOpenVolumeCallback> OpenVolumeCallback; + UInt64 Files; + UInt64 Offset; + + #ifndef _NO_CRYPTO + CMyComPtr<ICryptoGetTextPassword> GetTextPassword; + #endif + + MY_QUERYINTERFACE_BEGIN2(IArchiveOpenCallback) + MY_QUERYINTERFACE_ENTRY(IArchiveOpenVolumeCallback) + #ifndef _NO_CRYPTO + MY_QUERYINTERFACE_ENTRY(ICryptoGetTextPassword) + #endif + MY_QUERYINTERFACE_END + MY_ADDREF_RELEASE + + INTERFACE_IArchiveOpenCallback(;) + INTERFACE_IArchiveOpenVolumeCallback(;) + #ifndef _NO_CRYPTO + STDMETHOD(CryptoGetTextPassword)(BSTR *password); + #endif +}; + +#ifndef _NO_CRYPTO +STDMETHODIMP CArchiveOpenCallback_Offset::CryptoGetTextPassword(BSTR *password) +{ + COM_TRY_BEGIN + if (GetTextPassword) + return GetTextPassword->CryptoGetTextPassword(password); + return E_NOTIMPL; + COM_TRY_END +} +#endif + +STDMETHODIMP CArchiveOpenCallback_Offset::SetTotal(const UInt64 *, const UInt64 *) +{ + return S_OK; +} + +STDMETHODIMP CArchiveOpenCallback_Offset::SetCompleted(const UInt64 *, const UInt64 *bytes) +{ + if (!Callback) + return S_OK; + UInt64 value = Offset; + if (bytes) + value += *bytes; + return Callback->SetCompleted(&Files, &value); +} + +STDMETHODIMP CArchiveOpenCallback_Offset::GetProperty(PROPID propID, PROPVARIANT *value) +{ + if (OpenVolumeCallback) + return OpenVolumeCallback->GetProperty(propID, value); + NCOM::PropVariant_Clear(value); + return S_OK; + // return E_NOTIMPL; +} + +STDMETHODIMP CArchiveOpenCallback_Offset::GetStream(const wchar_t *name, IInStream **inStream) +{ + if (OpenVolumeCallback) + return OpenVolumeCallback->GetStream(name, inStream); + return S_FALSE; +} + +#endif + + +UInt32 GetOpenArcErrorFlags(const NCOM::CPropVariant &prop, bool *isDefinedProp) +{ + if (isDefinedProp != NULL) + *isDefinedProp = false; + + switch (prop.vt) + { + case VT_UI8: if (isDefinedProp) *isDefinedProp = true; return (UInt32)prop.uhVal.QuadPart; + case VT_UI4: if (isDefinedProp) *isDefinedProp = true; return prop.ulVal; + case VT_EMPTY: return 0; + default: throw 151199; + } +} + +void CArcErrorInfo::ClearErrors() +{ + // ErrorFormatIndex = -1; // we don't need to clear ErrorFormatIndex here !!! + + ThereIsTail = false; + UnexpecedEnd = false; + IgnoreTail = false; + // NonZerosTail = false; + ErrorFlags_Defined = false; + ErrorFlags = 0; + WarningFlags = 0; + TailSize = 0; + ErrorMessage.Empty(); + WarningMessage.Empty(); +} + +HRESULT CArc::ReadBasicProps(IInArchive *archive, UInt64 startPos, HRESULT openRes) +{ + // OkPhySize_Defined = false; + PhySizeDefined = false; + PhySize = 0; + Offset = 0; + AvailPhySize = FileSize - startPos; + + ErrorInfo.ClearErrors(); + { + NCOM::CPropVariant prop; + RINOK(archive->GetArchiveProperty(kpidErrorFlags, &prop)); + ErrorInfo.ErrorFlags = GetOpenArcErrorFlags(prop, &ErrorInfo.ErrorFlags_Defined); + } + { + NCOM::CPropVariant prop; + RINOK(archive->GetArchiveProperty(kpidWarningFlags, &prop)); + ErrorInfo.WarningFlags = GetOpenArcErrorFlags(prop); + } + + { + NCOM::CPropVariant prop; + RINOK(archive->GetArchiveProperty(kpidError, &prop)); + if (prop.vt != VT_EMPTY) + ErrorInfo.ErrorMessage = (prop.vt == VT_BSTR ? prop.bstrVal : L"Unknown error"); + } + + { + NCOM::CPropVariant prop; + RINOK(archive->GetArchiveProperty(kpidWarning, &prop)); + if (prop.vt != VT_EMPTY) + ErrorInfo.WarningMessage = (prop.vt == VT_BSTR ? prop.bstrVal : L"Unknown warning"); + } + + if (openRes == S_OK || ErrorInfo.IsArc_After_NonOpen()) + { + RINOK(Archive_GetArcProp_UInt(archive, kpidPhySize, PhySize, PhySizeDefined)); + /* + RINOK(Archive_GetArcProp_UInt(archive, kpidOkPhySize, OkPhySize, OkPhySize_Defined)); + if (!OkPhySize_Defined) + { + OkPhySize_Defined = PhySizeDefined; + OkPhySize = PhySize; + } + */ + + bool offsetDefined; + RINOK(Archive_GetArcProp_Int(archive, kpidOffset, Offset, offsetDefined)); + + Int64 globalOffset = startPos + Offset; + AvailPhySize = FileSize - globalOffset; + if (PhySizeDefined) + { + UInt64 endPos = globalOffset + PhySize; + if (endPos < FileSize) + { + AvailPhySize = PhySize; + ErrorInfo.ThereIsTail = true; + ErrorInfo.TailSize = FileSize - endPos; + } + else if (endPos > FileSize) + ErrorInfo.UnexpecedEnd = true; + } + } + + return S_OK; +} + +/* +static PrintNumber(const char *s, int n) +{ + char temp[100]; + sprintf(temp, "%s %d", s, n); + OutputDebugStringA(temp); +} +*/ + +HRESULT CArc::PrepareToOpen(const COpenOptions &op, unsigned formatIndex, CMyComPtr<IInArchive> &archive) +{ + // OutputDebugStringA("a1"); + // PrintNumber("formatIndex", formatIndex); + + RINOK(op.codecs->CreateInArchive(formatIndex, archive)); + // OutputDebugStringA("a2"); + if (!archive) + return S_OK; + + #ifdef EXTERNAL_CODECS + if (op.codecs->NeedSetLibCodecs) + { + const CArcInfoEx &ai = op.codecs->Formats[formatIndex]; + if (ai.LibIndex >= 0 ? + !op.codecs->Libs[ai.LibIndex].SetCodecs : + !op.codecs->Libs.IsEmpty()) + { + CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo; + archive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo); + if (setCompressCodecsInfo) + { + RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(op.codecs)); + } + } + } + #endif + + + #ifndef _SFX + + const CArcInfoEx &ai = op.codecs->Formats[formatIndex]; + + // OutputDebugStringW(ai.Name); + // OutputDebugStringA("a3"); + + if (ai.Flags_PreArc()) + { + /* we notify parsers that extract executables, that they don't need + to open archive, if there is tail after executable (for SFX cases) */ + CMyComPtr<IArchiveAllowTail> allowTail; + archive.QueryInterface(IID_IArchiveAllowTail, (void **)&allowTail); + if (allowTail) + allowTail->AllowTail(BoolToInt(true)); + } + + if (op.props) + { + /* + FOR_VECTOR (y, op.props) + { + const COptionalOpenProperties &optProps = (*op.props)[y]; + if (optProps.FormatName.IsEmpty() || optProps.FormatName.CompareNoCase(ai.Name) == 0) + { + RINOK(SetProperties(archive, optProps.Props)); + break; + } + } + */ + RINOK(SetProperties(archive, *op.props)); + } + + #endif + return S_OK; +} + +#ifndef _SFX + +static HRESULT ReadParseItemProps(IInArchive *archive, const CArcInfoEx &ai, NArchive::NParser::CParseItem &pi) +{ + pi.Extension = ai.GetMainExt(); + pi.FileTime_Defined = false; + pi.ArcType = ai.Name; + + RINOK(Archive_GetArcBoolProp(archive, kpidIsNotArcType, pi.IsNotArcType)); + + // RINOK(Archive_GetArcBoolProp(archive, kpidIsSelfExe, pi.IsSelfExe)); + pi.IsSelfExe = ai.Flags_PreArc(); + + { + NCOM::CPropVariant prop; + RINOK(archive->GetArchiveProperty(kpidMTime, &prop)); + if (prop.vt == VT_FILETIME) + { + pi.FileTime_Defined = true; + pi.FileTime = prop.filetime; + } + } + + if (!pi.FileTime_Defined) + { + NCOM::CPropVariant prop; + RINOK(archive->GetArchiveProperty(kpidCTime, &prop)); + if (prop.vt == VT_FILETIME) + { + pi.FileTime_Defined = true; + pi.FileTime = prop.filetime; + } + } + + { + NCOM::CPropVariant prop; + RINOK(archive->GetArchiveProperty(kpidName, &prop)); + if (prop.vt == VT_BSTR) + { + pi.Name.SetFromBstr(prop.bstrVal); + pi.Extension.Empty(); + } + else + { + RINOK(archive->GetArchiveProperty(kpidExtension, &prop)); + if (prop.vt == VT_BSTR) + pi.Extension.SetFromBstr(prop.bstrVal); + } + } + + { + NCOM::CPropVariant prop; + RINOK(archive->GetArchiveProperty(kpidShortComment, &prop)); + if (prop.vt == VT_BSTR) + pi.Comment.SetFromBstr(prop.bstrVal); + } + + + UInt32 numItems; + RINOK(archive->GetNumberOfItems(&numItems)); + + // pi.NumSubFiles = numItems; + // RINOK(Archive_GetArcProp_UInt(archive, kpidUnpackSize, pi.UnpackSize, pi.UnpackSize_Defined)); + // if (!pi.UnpackSize_Defined) + { + pi.NumSubFiles = 0; + pi.NumSubDirs = 0; + pi.UnpackSize = 0; + for (UInt32 i = 0; i < numItems; i++) + { + UInt64 size = 0; + bool defined = false; + Archive_GetItem_Size(archive, i, size, defined); + if (defined) + { + pi.UnpackSize_Defined = true; + pi.UnpackSize += size; + } + + bool isDir = false; + Archive_IsItem_Dir(archive, i, isDir); + if (isDir) + pi.NumSubDirs++; + else + pi.NumSubFiles++; + } + if (pi.NumSubDirs != 0) + pi.NumSubDirs_Defined = true; + pi.NumSubFiles_Defined = true; + } + + return S_OK; +} + +#endif + +HRESULT CArc::CheckZerosTail(const COpenOptions &op, UInt64 offset) +{ + if (!op.stream) + return S_OK; + RINOK(op.stream->Seek(offset, STREAM_SEEK_SET, NULL)); + const UInt32 kBufSize = 1 << 11; + Byte buf[kBufSize]; + + for (;;) + { + UInt32 processed = 0; + RINOK(op.stream->Read(buf, kBufSize, &processed)); + if (processed == 0) + { + // ErrorInfo.NonZerosTail = false; + ErrorInfo.IgnoreTail = true; + return S_OK; + } + for (size_t i = 0; i < processed; i++) + { + if (buf[i] != 0) + { + // ErrorInfo.IgnoreTail = false; + // ErrorInfo.NonZerosTail = true; + return S_OK; + } + } + } +} + +#ifndef _SFX + +class CExtractCallback_To_OpenCallback: + public IArchiveExtractCallback, + public ICompressProgressInfo, + public CMyUnknownImp +{ +public: + CMyComPtr<IArchiveOpenCallback> Callback; + UInt64 Files; + UInt64 Offset; + + MY_UNKNOWN_IMP2(IArchiveExtractCallback, ICompressProgressInfo) + INTERFACE_IArchiveExtractCallback(;) + STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); + void Init(IArchiveOpenCallback *callback) + { + Callback = callback; + Files = 0; + Offset = 0; + } +}; + +STDMETHODIMP CExtractCallback_To_OpenCallback::SetTotal(UInt64 /* size */) +{ + return S_OK; +} + +STDMETHODIMP CExtractCallback_To_OpenCallback::SetCompleted(const UInt64 * /* completeValue */) +{ + return S_OK; +} + +STDMETHODIMP CExtractCallback_To_OpenCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 * /* outSize */) +{ + if (Callback) + { + UInt64 value = Offset; + if (inSize) + value += *inSize; + return Callback->SetCompleted(&Files, &value); + } + return S_OK; +} + +STDMETHODIMP CExtractCallback_To_OpenCallback::GetStream(UInt32 /* index */, ISequentialOutStream **outStream, Int32 /* askExtractMode */) +{ + *outStream = 0; + return S_OK; +} + +STDMETHODIMP CExtractCallback_To_OpenCallback::PrepareOperation(Int32 /* askExtractMode */) +{ + return S_OK; +} + +STDMETHODIMP CExtractCallback_To_OpenCallback::SetOperationResult(Int32 /* operationResult */) +{ + return S_OK; +} + +static HRESULT OpenArchiveSpec(IInArchive *archive, bool needPhySize, + IInStream *stream, const UInt64 *maxCheckStartPosition, + IArchiveOpenCallback *openCallback, + IArchiveExtractCallback *extractCallback) +{ + /* + if (needPhySize) + { + CMyComPtr<IArchiveOpen2> open2; + archive->QueryInterface(IID_IArchiveOpen2, (void **)&open2); + if (open2) + return open2->ArcOpen2(stream, kOpenFlags_RealPhySize, openCallback); + } + */ + RINOK(archive->Open(stream, maxCheckStartPosition, openCallback)); + if (needPhySize) + { + bool phySize_Defined = false; + UInt64 phySize = 0; + RINOK(Archive_GetArcProp_UInt(archive, kpidPhySize, phySize, phySize_Defined)); + if (phySize_Defined) + return S_OK; + + bool phySizeCantBeDetected = false;; + RINOK(Archive_GetArcBoolProp(archive, kpidPhySizeCantBeDetected, phySizeCantBeDetected)); + + if (!phySizeCantBeDetected) + { + RINOK(archive->Extract(0, (UInt32)(Int32)-1, BoolToInt(true), extractCallback)); + } + } + return S_OK; +} + +static int FindFormatForArchiveType(CCodecs *codecs, CIntVector orderIndices, const char *name) +{ + FOR_VECTOR (i, orderIndices) + if (StringsAreEqualNoCase_Ascii(codecs->Formats[orderIndices[i]].Name, name)) + return i; + return -1; +} + +#endif + +HRESULT CArc::OpenStream2(const COpenOptions &op) +{ + // fprintf(stdout, "\nOpen: %S", Path); fflush(stdout); + + Archive.Release(); + GetRawProps.Release(); + GetRootProps.Release(); + + ErrorInfo.ClearErrors(); + ErrorInfo.ErrorFormatIndex = -1; + + IsParseArc = false; + ArcStreamOffset = 0; + + // OutputDebugStringA("1"); + // OutputDebugStringW(Path); + const UString fileName = ExtractFileNameFromPath(Path); UString extension; { - int dotPos = fileName.ReverseFind(L'.'); + int dotPos = fileName.ReverseFind_Dot(); if (dotPos >= 0) - extension = fileName.Mid(dotPos + 1); + extension = fileName.Ptr(dotPos + 1); } + CIntVector orderIndices; + + bool searchMarkerInHandler = false; + #ifdef _SFX + searchMarkerInHandler = true; + #endif + + CBoolArr isMainFormatArr(op.codecs->Formats.Size()); + { + FOR_VECTOR(i, op.codecs->Formats) + isMainFormatArr[i] = false; + } + + UInt64 maxStartOffset = + op.openType.MaxStartOffset_Defined ? + op.openType.MaxStartOffset : + kMaxCheckStartPosition; + + #ifndef _SFX + bool isUnknownExt = false; + #endif + + bool isForced = false; + unsigned numMainTypes = 0; + int formatIndex = op.openType.FormatIndex; + if (formatIndex >= 0) + { + isForced = true; orderIndices.Add(formatIndex); + numMainTypes = 1; + isMainFormatArr[(unsigned)formatIndex] = true; + + searchMarkerInHandler = true; + } else { + unsigned numFinded = 0; + #ifndef _SFX + bool isPrearcExt = false; + #endif + + { + FOR_VECTOR (i, op.codecs->Formats) + { + const CArcInfoEx &ai = op.codecs->Formats[i]; + + if (IgnoreSplit || !op.openType.CanReturnArc) + if (ai.IsSplit()) + continue; + if (op.excludedFormats->FindInSorted(i) >= 0) + continue; + + #ifndef _SFX + if (IsPreArcFormat(ai)) + isPrearcExt = true; + #endif + + if (ai.FindExtension(extension) >= 0) + { + // PrintNumber("orderIndices.Insert", i); + orderIndices.Insert(numFinded++, i); + isMainFormatArr[i] = true; + } + else + orderIndices.Add(i); + } + } + + if (!op.stream) + { + if (numFinded != 1) + return E_NOTIMPL; + orderIndices.DeleteFrom(1); + } + // PrintNumber("numFinded", numFinded ); + + /* + if (op.openOnlySpecifiedByExtension) + { + if (numFinded != 0 && !IsExeExt(extension)) + orderIndices.DeleteFrom(numFinded); + } + */ + + #ifndef _SFX + + if (op.stream && orderIndices.Size() >= 2) + { + RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); + CByteBuffer byteBuffer; + CIntVector orderIndices2; + if (numFinded == 0 || IsExeExt(extension)) + { + // signature search was here + } + else if (extension.IsEqualTo("000") || extension.IsEqualTo("001")) + { + int i = FindFormatForArchiveType(op.codecs, orderIndices, "rar"); + if (i >= 0) + { + const size_t kBufSize = (1 << 10); + byteBuffer.Alloc(kBufSize); + size_t processedSize = kBufSize; + RINOK(ReadStream(op.stream, byteBuffer, &processedSize)); + if (processedSize >= 16) + { + const Byte *buf = byteBuffer; + const Byte kRarHeader[] = { 0x52 , 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00 }; + if (TestSignature(buf, kRarHeader, 7) && buf[9] == 0x73 && (buf[10] & 1) != 0) + { + orderIndices2.Add(orderIndices[i]); + orderIndices[i] = -1; + if (i >= (int)numFinded) + numFinded++; + } + } + } + } + else + { + const size_t kBufSize = (1 << 10); + byteBuffer.Alloc(kBufSize); + size_t processedSize = kBufSize; + RINOK(ReadStream(op.stream, byteBuffer, &processedSize)); + if (processedSize == 0) + return S_FALSE; + + /* + check type order: + 1) matched extension, no signuature + 2) matched extension, matched signuature + // 3) no signuature + // 4) matched signuature + */ + + MakeCheckOrder(op.codecs, orderIndices, numFinded, orderIndices2, NULL, 0); + MakeCheckOrder(op.codecs, orderIndices, numFinded, orderIndices2, byteBuffer, processedSize); + // MakeCheckOrder(op.codecs, orderIndices, orderIndices.Size(), orderIndices2, NULL, 0); + // MakeCheckOrder(op.codecs, orderIndices, orderIndices.Size(), orderIndices2, byteBuffer, processedSize); + } + + FOR_VECTOR (i, orderIndices) + { + int val = orderIndices[i]; + if (val != -1) + orderIndices2.Add(val); + } + orderIndices = orderIndices2; + } + + if (orderIndices.Size() >= 2) + { + int iIso = FindFormatForArchiveType(op.codecs, orderIndices, "iso"); + int iUdf = FindFormatForArchiveType(op.codecs, orderIndices, "udf"); + if (iUdf > iIso && iIso >= 0) + { + int isoIndex = orderIndices[iIso]; + int udfIndex = orderIndices[iUdf]; + orderIndices[iUdf] = isoIndex; + orderIndices[iIso] = udfIndex; + } + } + + numMainTypes = numFinded; + isUnknownExt = (numMainTypes == 0) || isPrearcExt; + + #else // _SFX + + numMainTypes = orderIndices.Size(); + + // we need correct numMainTypes for mutlivolume SFX (if some volume is missing) + if (numFinded != 0) + numMainTypes = numFinded; + + #endif + } + + UInt64 fileSize = 0; + if (op.stream) + { + RINOK(op.stream->Seek(0, STREAM_SEEK_END, &fileSize)); + RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); + } + FileSize = fileSize; + + + #ifndef _SFX + + CBoolArr skipFrontalFormat(op.codecs->Formats.Size()); + { + FOR_VECTOR(i, op.codecs->Formats) + skipFrontalFormat[i] = false; + } + + #endif + + const COpenType &mode = op.openType; + + + - int i; - int numFinded = 0; - for (i = 0; i < codecs->Formats.Size(); i++) - if (codecs->Formats[i].FindExtension(extension) >= 0) - orderIndices.Insert(numFinded++, i); - else - orderIndices.Add(i); - if (!stream) + if (mode.CanReturnArc) { - if (numFinded != 1) - return E_NOTIMPL; - orderIndices.DeleteFrom(1); + // ---------- OPEN main type by extenssion ---------- + + unsigned numCheckTypes = orderIndices.Size(); + if (formatIndex >= 0) + numCheckTypes = numMainTypes; + + for (unsigned i = 0; i < numCheckTypes; i++) + { + FormatIndex = orderIndices[i]; + + bool exactOnly = false; + + #ifndef _SFX + + const CArcInfoEx &ai = op.codecs->Formats[FormatIndex]; + // OutputDebugStringW(ai.Name); + if (i >= numMainTypes) + { + if (!ai.Flags_BackwardOpen() + // && !ai.Flags_PureStartOpen() + ) + continue; + exactOnly = true; + } + + #endif + + // Some handlers do not set total bytes. So we set it here + if (op.callback) + RINOK(op.callback->SetTotal(NULL, &fileSize)); + + if (op.stream) + { + RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); + } + + CMyComPtr<IInArchive> archive; + + RINOK(PrepareToOpen(op, FormatIndex, archive)); + if (!archive) + continue; + + HRESULT result; + if (op.stream) + { + UInt64 searchLimit = (!exactOnly && searchMarkerInHandler) ? maxStartOffset: 0; + result = archive->Open(op.stream, &searchLimit, op.callback); + } + else + { + CMyComPtr<IArchiveOpenSeq> openSeq; + archive.QueryInterface(IID_IArchiveOpenSeq, (void **)&openSeq); + if (!openSeq) + return E_NOTIMPL; + result = openSeq->OpenSeq(op.seqStream); + } + + RINOK(ReadBasicProps(archive, 0, result)); + + if (result == S_FALSE) + { + bool isArc = ErrorInfo.IsArc_After_NonOpen(); + + #ifndef _SFX + // if it's archive, we allow another open attempt for parser + if (!mode.CanReturnParser || !isArc) + skipFrontalFormat[(unsigned)FormatIndex] = true; + #endif + + if (exactOnly) + continue; + + if (i == 0 && numMainTypes == 1) + { + // we set NonOpenErrorInfo, only if there is only one main format (defined by extension). + ErrorInfo.ErrorFormatIndex = FormatIndex; + NonOpen_ErrorInfo = ErrorInfo; + + if (!mode.CanReturnParser && isArc) + { + // if (formatIndex < 0 && !searchMarkerInHandler) + { + // if bad archive was detected, we don't need additional open attempts + #ifndef _SFX + if (!IsPreArcFormat(ai) /* || !mode.SkipSfxStub */) + #endif + return S_FALSE; + } + } + } + + /* + #ifndef _SFX + if (IsExeExt(extension) || ai.Flags_PreArc()) + { + // openOnlyFullArc = false; + // canReturnTailArc = true; + // limitSignatureSearch = true; + } + #endif + */ + + continue; + } + + RINOK(result); + + #ifndef _SFX + + bool isMainFormat = isMainFormatArr[(unsigned)FormatIndex]; + const COpenSpecFlags &specFlags = mode.GetSpec(isForced, isMainFormat, isUnknownExt); + + bool thereIsTail = ErrorInfo.ThereIsTail; + if (thereIsTail && mode.ZerosTailIsAllowed) + { + RINOK(CheckZerosTail(op, Offset + PhySize)); + if (ErrorInfo.IgnoreTail) + thereIsTail = false; + } + + if (Offset > 0) + { + if (exactOnly + || !searchMarkerInHandler + || !specFlags.CanReturn_NonStart() + || (mode.MaxStartOffset_Defined && (UInt64)Offset > mode.MaxStartOffset)) + continue; + } + if (thereIsTail) + { + if (Offset > 0) + { + if (!specFlags.CanReturnMid) + continue; + } + else if (!specFlags.CanReturnFrontal) + continue; + } + + if (Offset > 0 || thereIsTail) + { + if (formatIndex < 0) + { + if (IsPreArcFormat(ai)) + { + // openOnlyFullArc = false; + // canReturnTailArc = true; + /* + if (mode.SkipSfxStub) + limitSignatureSearch = true; + */ + // if (mode.SkipSfxStub) + { + // skipFrontalFormat[FormatIndex] = true; + continue; + } + } + } + } + + #endif + + Archive = archive; + return S_OK; + } } + + #ifndef _SFX - if (orderIndices.Size() >= 2 && (numFinded == 0 || extension.CompareNoCase(L"exe") == 0)) + + if (!op.stream) + return S_FALSE; + + if (formatIndex >= 0 && !mode.CanReturnParser) { - CIntVector orderIndices2; + if (mode.MaxStartOffset_Defined) + { + if (mode.MaxStartOffset == 0) + return S_FALSE; + } + else + { + const CArcInfoEx &ai = op.codecs->Formats[formatIndex]; + if (ai.FindExtension(extension) >= 0) + { + const CArcInfoEx &ai = op.codecs->Formats[formatIndex]; + if (ai.Flags_FindSignature() && searchMarkerInHandler) + return S_FALSE; + } + } + } + + NArchive::NParser::CHandler *handlerSpec = new NArchive::NParser::CHandler; + CMyComPtr<IInArchive> handler = handlerSpec; + + CExtractCallback_To_OpenCallback *extractCallback_To_OpenCallback_Spec = new CExtractCallback_To_OpenCallback; + CMyComPtr<IArchiveExtractCallback> extractCallback_To_OpenCallback = extractCallback_To_OpenCallback_Spec; + extractCallback_To_OpenCallback_Spec->Init(op.callback); + + { + // ---------- Check all possible START archives ---------- + // this code is better for full file archives than Parser's code. + CByteBuffer byteBuffer; - const size_t kBufferSize = (1 << 21); - byteBuffer.SetCapacity(kBufferSize); - RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL)); - size_t processedSize = kBufferSize; - RINOK(ReadStream(stream, byteBuffer, &processedSize)); - if (processedSize == 0) - return S_FALSE; + bool endOfFile = false; + size_t processedSize; + { + size_t bufSize = 1 << 20; // it must be larger than max signature offset or IsArcFunc offset ((1 << 19) + x for UDF) + if (bufSize > fileSize) + { + bufSize = (size_t)fileSize; + endOfFile = true; + } + byteBuffer.Alloc(bufSize); + RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); + processedSize = bufSize; + RINOK(ReadStream(op.stream, byteBuffer, &processedSize)); + if (processedSize == 0) + return S_FALSE; + if (processedSize < bufSize) + endOfFile = true; + } + CUIntVector sortedFormats; + + unsigned i; + + int splitIndex = -1; - const Byte *buf = byteBuffer; - CByteBuffer hashBuffer; - const UInt32 kNumVals = 1 << (kNumHashBytes * 8); - hashBuffer.SetCapacity(kNumVals); - Byte *hash = hashBuffer; - memset(hash, 0xFF, kNumVals); - Byte prevs[256]; - if (orderIndices.Size() >= 256) - return S_FALSE; - int i; for (i = 0; i < orderIndices.Size(); i++) { - const CArcInfoEx &ai = codecs->Formats[orderIndices[i]]; - const CByteBuffer &sig = ai.StartSignature; - if (sig.GetCapacity() < kNumHashBytes) + unsigned form = orderIndices[i]; + if (skipFrontalFormat[form]) + continue; + const CArcInfoEx &ai = op.codecs->Formats[form]; + if (ai.IsSplit()) + { + splitIndex = form; continue; - UInt32 v = HASH_VAL(sig, 0); - prevs[i] = hash[v]; - hash[v] = (Byte)i; + } + + if (ai.IsArcFunc) + { + UInt32 isArcRes = ai.IsArcFunc(byteBuffer, processedSize); + if (isArcRes == k_IsArc_Res_NO) + continue; + if (isArcRes == k_IsArc_Res_NEED_MORE && endOfFile) + continue; + // if (isArcRes == k_IsArc_Res_YES_LOW_PROB) continue; + sortedFormats.Insert(0, form); + continue; + } + + bool isNewStyleSignature = IsNewStyleSignature(ai); + bool needCheck = !isNewStyleSignature + || ai.Signatures.IsEmpty() + || ai.Flags_PureStartOpen() + || ai.Flags_StartOpen() + || ai.Flags_BackwardOpen(); + + if (isNewStyleSignature && !ai.Signatures.IsEmpty()) + { + unsigned k; + for (k = 0; k < ai.Signatures.Size(); k++) + { + const CByteBuffer &sig = ai.Signatures[k]; + UInt32 signatureEnd = ai.SignatureOffset + (UInt32)sig.Size(); + if (processedSize < signatureEnd) + { + if (!endOfFile) + needCheck = true; + } + else if (memcmp(sig, byteBuffer + ai.SignatureOffset, sig.Size()) == 0) + break; + } + if (k != ai.Signatures.Size()) + { + sortedFormats.Insert(0, form); + continue; + } + } + if (needCheck) + sortedFormats.Add(form); } - processedSize -= (kNumHashBytes - 1); - for (UInt32 pos = 0; pos < processedSize; pos++) + if (splitIndex >= 0) + sortedFormats.Insert(0, splitIndex); + + for (i = 0; i < sortedFormats.Size(); i++) { - for (; pos < processedSize && hash[HASH_VAL(buf, pos)] == 0xFF; pos++); - if (pos == processedSize) - break; - UInt32 v = HASH_VAL(buf, pos); - Byte *ptr = &hash[v]; - int i = *ptr; - do + FormatIndex = sortedFormats[i]; + const CArcInfoEx &ai = op.codecs->Formats[FormatIndex]; + + if (op.callback) + RINOK(op.callback->SetTotal(NULL, &fileSize)); + + RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); + + CMyComPtr<IInArchive> archive; + RINOK(PrepareToOpen(op, FormatIndex, archive)); + if (!archive) + continue; + + PRF(printf("\nSorted Open %S", (const wchar_t *)ai.Name)); + HRESULT result; { - int index = orderIndices[i]; - const CArcInfoEx &ai = codecs->Formats[index]; - const CByteBuffer &sig = ai.StartSignature; - if (sig.GetCapacity() != 0 && pos + sig.GetCapacity() <= processedSize + (kNumHashBytes - 1) && - TestSignature(buf + pos, sig, sig.GetCapacity())) + UInt64 searchLimit = 0; + /* + if (mode.CanReturnArc) + result = archive->Open(op.stream, &searchLimit, op.callback); + else + */ + result = OpenArchiveSpec(archive, !mode.CanReturnArc, op.stream, &searchLimit, op.callback, extractCallback_To_OpenCallback); + } + + if (result == S_FALSE) + { + skipFrontalFormat[(unsigned)FormatIndex] = true; + // FIXME: maybe we must use LenIsUnknown. + // printf(" OpenForSize Error"); + continue; + } + RINOK(result); + + RINOK(ReadBasicProps(archive, 0, result)); + + if (Offset > 0) + { + continue; // good handler doesn't return such Offset > 0 + // but there are some cases like false prefixed PK00 archive, when + // we can support it? + } + + NArchive::NParser::CParseItem pi; + pi.Offset = Offset; + pi.Size = AvailPhySize; + + // bool needScan = false; + + if (!PhySizeDefined) + { + // it's for Z format + pi.LenIsUnknown = true; + // needScan = true; + // phySize = arcRem; + // nextNeedCheckStartOpen = false; + } + + /* + if (OkPhySize_Defined) + pi.OkSize = pi.OkPhySize; + else + pi.OkSize = pi.Size; + */ + + pi.NormalizeOffset(); + // printf(" phySize = %8d", (unsigned)phySize); + + + if (mode.CanReturnArc) + { + bool isMainFormat = isMainFormatArr[(unsigned)FormatIndex]; + const COpenSpecFlags &specFlags = mode.GetSpec(isForced, isMainFormat, isUnknownExt); + bool openCur = false; + + if (!ErrorInfo.ThereIsTail) + openCur = true; + else { - orderIndices2.Add(index); - orderIndices[i] = 0xFF; - *ptr = prevs[i]; + if (mode.ZerosTailIsAllowed) + { + RINOK(CheckZerosTail(op, Offset + PhySize)); + if (ErrorInfo.IgnoreTail) + openCur = true; + } + if (!openCur) + { + openCur = specFlags.CanReturnFrontal; + if (formatIndex < 0) // format is not forced + { + if (IsPreArcFormat(ai)) + { + // if (mode.SkipSfxStub) + { + openCur = false; + } + } + } + } + } + + if (openCur) + { + InStream = op.stream; + Archive = archive; + return S_OK; } - else - ptr = &prevs[i]; - i = *ptr; } - while (i != 0xFF); + + skipFrontalFormat[(unsigned)FormatIndex] = true; + + + // if (!mode.CanReturnArc) + /* + if (!ErrorInfo.ThereIsTail) + continue; + */ + if (pi.Offset == 0 && !pi.LenIsUnknown && pi.Size >= FileSize) + continue; + + // printf("\nAdd offset = %d", (int)pi.Offset); + RINOK(ReadParseItemProps(archive, ai, pi)); + handlerSpec->AddItem(pi); } - - for (i = 0; i < orderIndices.Size(); i++) + } + + + + + + // ---------- PARSER ---------- + + CUIntVector arc2sig; // formatIndex to signatureIndex + CUIntVector sig2arc; // signatureIndex to formatIndex; + { + unsigned sum = 0; + FOR_VECTOR (i, op.codecs->Formats) { - int val = orderIndices[i]; - if (val != 0xFF) - orderIndices2.Add(val); + arc2sig.Add(sum); + const CObjectVector<CByteBuffer> &sigs = op.codecs->Formats[i].Signatures; + sum += sigs.Size(); + FOR_VECTOR (k, sigs) + sig2arc.Add(i); } - orderIndices = orderIndices2; } - else if (extension == L"000" || extension == L"001") + { - CByteBuffer byteBuffer; - const size_t kBufferSize = (1 << 10); - byteBuffer.SetCapacity(kBufferSize); - Byte *buffer = byteBuffer; - RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL)); - size_t processedSize = kBufferSize; - RINOK(ReadStream(stream, buffer, &processedSize)); - if (processedSize >= 16) - { - Byte kRarHeader[] = {0x52 , 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00}; - if (TestSignature(buffer, kRarHeader, 7) && buffer[9] == 0x73 && (buffer[10] & 1) != 0) - { - for (int i = 0; i < orderIndices.Size(); i++) - { - int index = orderIndices[i]; - const CArcInfoEx &ai = codecs->Formats[index]; - if (ai.Name.CompareNoCase(L"rar") != 0) + const size_t kBeforeSize = 1 << 16; + const size_t kAfterSize = 1 << 20; + const size_t kBufSize = 1 << 22; // it must be more than kBeforeSize + kAfterSize + + const UInt32 kNumVals = (UInt32)1 << (kNumHashBytes * 8); + CByteArr hashBuffer(kNumVals); + Byte *hash = hashBuffer; + memset(hash, 0xFF, kNumVals); + Byte prevs[256]; + memset(prevs, 0xFF, sizeof(prevs)); + if (sig2arc.Size() >= 0xFF) + return S_FALSE; + + CUIntVector difficultFormats; + CBoolArr difficultBools(256); + { + for (unsigned i = 0; i < 256; i++) + difficultBools[i] = false; + } + + bool thereAreHandlersForSearch = false; + + // UInt32 maxSignatureEnd = 0; + + FOR_VECTOR (i, orderIndices) + { + int index = orderIndices[i]; + if (index < 0) + continue; + const CArcInfoEx &ai = op.codecs->Formats[index]; + bool isDifficult = false; + // if (ai.Version < 0x91F) // we don't use parser with old DLL (before 9.31) + if (!ai.NewInterface) + isDifficult = true; + else + { + if (ai.Flags_StartOpen()) + isDifficult = true; + FOR_VECTOR (k, ai.Signatures) + { + const CByteBuffer &sig = ai.Signatures[k]; + /* + UInt32 signatureEnd = ai.SignatureOffset + (UInt32)sig.Size(); + if (maxSignatureEnd < signatureEnd) + maxSignatureEnd = signatureEnd; + */ + if (sig.Size() < kNumHashBytes) + { + isDifficult = true; continue; - orderIndices.Delete(i--); - orderIndices.Insert(0, index); - break; + } + thereAreHandlersForSearch = true; + UInt32 v = HASH_VAL(sig, 0); + unsigned sigIndex = arc2sig[(unsigned)index] + k; + prevs[sigIndex] = hash[v]; + hash[v] = (Byte)sigIndex; } } + if (isDifficult) + { + difficultFormats.Add(index); + difficultBools[index] = true; + } } - } - if (orderIndices.Size() >= 2) - { - int isoIndex = codecs->FindFormatForArchiveType(L"iso"); - int udfIndex = codecs->FindFormatForArchiveType(L"udf"); - int iIso = -1; - int iUdf = -1; - for (int i = 0; i < orderIndices.Size(); i++) + + if (!thereAreHandlersForSearch) { - if (orderIndices[i] == isoIndex) iIso = i; - if (orderIndices[i] == udfIndex) iUdf = i; + // openOnlyFullArc = true; + // canReturnTailArc = true; } - if (iUdf > iIso && iIso >= 0) + + RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); + + CLimitedCachedInStream *limitedStreamSpec = new CLimitedCachedInStream; + CMyComPtr<IInStream> limitedStream = limitedStreamSpec; + limitedStreamSpec->SetStream(op.stream); + + CArchiveOpenCallback_Offset *openCallback_Offset_Spec = NULL; + CMyComPtr<IArchiveOpenCallback> openCallback_Offset; + if (op.callback) { - orderIndices[iUdf] = isoIndex; - orderIndices[iIso] = udfIndex; + openCallback_Offset_Spec = new CArchiveOpenCallback_Offset; + openCallback_Offset = openCallback_Offset_Spec; + openCallback_Offset_Spec->Callback = op.callback; + openCallback_Offset_Spec->Callback.QueryInterface(IID_IArchiveOpenVolumeCallback, &openCallback_Offset_Spec->OpenVolumeCallback); + #ifndef _NO_CRYPTO + openCallback_Offset_Spec->Callback.QueryInterface(IID_ICryptoGetTextPassword, &openCallback_Offset_Spec->GetTextPassword); + #endif } - } - #endif - } + if (op.callback) + RINOK(op.callback->SetTotal(NULL, &fileSize)); + + CByteBuffer &byteBuffer = limitedStreamSpec->Buffer; + byteBuffer.Alloc(kBufSize); - for (int i = 0; i < orderIndices.Size(); i++) - { - if (stream) + UInt64 callbackPrev = 0; + bool needCheckStartOpen = true; // = true, if we need to test all archives types for current pos. + + bool endOfFile = false; + UInt64 bufPhyPos = 0; + size_t bytesInBuf = 0; + // UInt64 prevPos = 0; + + // ---------- Main Scan Loop ---------- + + UInt64 pos = 0; + + if (!mode.EachPos && handlerSpec->_items.Size() == 1) { - RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL)); + NArchive::NParser::CParseItem &pi = handlerSpec->_items[0]; + if (!pi.LenIsUnknown && pi.Offset == 0) + pos = pi.Size; } - CMyComPtr<IInArchive> archive; - FormatIndex = orderIndices[i]; - RINOK(codecs->CreateInArchive(FormatIndex, archive)); - if (!archive) - continue; - - #ifdef EXTERNAL_CODECS + for (;;) { - CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo; - archive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo); - if (setCompressCodecsInfo) + // printf("\nPos = %d", (int)pos); + UInt64 posInBuf = pos - bufPhyPos; + + // if (pos > ((UInt64)1 << 35)) break; + + if (!endOfFile) { - RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs)); + if (bytesInBuf < kBufSize) + { + size_t processedSize = kBufSize - bytesInBuf; + // printf("\nRead ask = %d", (unsigned)processedSize); + UInt64 seekPos = bufPhyPos + bytesInBuf; + RINOK(op.stream->Seek(bufPhyPos + bytesInBuf, STREAM_SEEK_SET, NULL)); + RINOK(ReadStream(op.stream, byteBuffer + bytesInBuf, &processedSize)); + // printf(" processed = %d", (unsigned)processedSize); + if (processedSize == 0) + { + fileSize = seekPos; + endOfFile = true; + } + else + { + bytesInBuf += processedSize; + limitedStreamSpec->SetCache(processedSize, (size_t)bufPhyPos); + } + continue; + } + + if (bytesInBuf < posInBuf) + { + UInt64 skipSize = posInBuf - bytesInBuf; + if (skipSize <= kBeforeSize) + { + size_t keepSize = (size_t)(kBeforeSize - skipSize); + // printf("\nmemmove skip = %d", (int)keepSize); + memmove(byteBuffer, byteBuffer + bytesInBuf - keepSize, keepSize); + bytesInBuf = keepSize; + bufPhyPos = pos - keepSize; + continue; + } + // printf("\nSkip %d", (int)(skipSize - kBeforeSize)); + // RINOK(op.stream->Seek(skipSize - kBeforeSize, STREAM_SEEK_CUR, NULL)); + bytesInBuf = 0; + bufPhyPos = pos - kBeforeSize; + continue; + } + + if (bytesInBuf - posInBuf < kAfterSize) + { + size_t beg = (size_t)posInBuf - kBeforeSize; + // printf("\nmemmove for after beg = %d", (int)beg); + memmove(byteBuffer, byteBuffer + beg, bytesInBuf - beg); + bufPhyPos += beg; + bytesInBuf -= beg; + continue; + } } + + bool useOffsetCallback = false; + if (openCallback_Offset) + { + openCallback_Offset_Spec->Files = handlerSpec->_items.Size(); + openCallback_Offset_Spec->Offset = pos; + + useOffsetCallback = (!op.openType.CanReturnArc || handlerSpec->_items.Size() > 1); + + if (pos >= callbackPrev + (1 << 23)) + { + RINOK(openCallback_Offset_Spec->SetCompleted(NULL, NULL)); + callbackPrev = pos; + } + } + + { + UInt64 endPos = bufPhyPos + bytesInBuf; + if (fileSize < endPos) + { + FileSize = fileSize; // why ???? + fileSize = endPos; + } + } + + size_t availSize = bytesInBuf - (size_t)posInBuf; + if (availSize < kNumHashBytes) + break; + size_t scanSize = availSize - + ((availSize >= kAfterSize) ? kAfterSize : kNumHashBytes); + + { + /* + UInt64 scanLimit = openOnlyFullArc ? + maxSignatureEnd : + op.openType.ScanSize + maxSignatureEnd; + */ + if (!mode.CanReturnParser) + { + if (pos > maxStartOffset) + break; + UInt64 remScan = maxStartOffset - pos; + if (scanSize > remScan) + scanSize = (size_t)remScan; + } + } + + scanSize++; + + const Byte *buf = byteBuffer + (size_t)posInBuf; + size_t ppp = 0; + + if (!needCheckStartOpen) + { + for (; ppp < scanSize && hash[HASH_VAL(buf, ppp)] == 0xFF; ppp++); + pos += ppp; + if (ppp == scanSize) + continue; + } + + UInt32 v = HASH_VAL(buf, ppp); + bool nextNeedCheckStartOpen = true; + unsigned i = hash[v]; + unsigned indexOfDifficult = 0; + + // ---------- Open Loop for Current Pos ---------- + bool wasOpen = false; + + for (;;) + { + unsigned index; + bool isDifficult; + if (needCheckStartOpen && indexOfDifficult < difficultFormats.Size()) + { + index = difficultFormats[indexOfDifficult++]; + isDifficult = true; + } + else + { + if (i == 0xFF) + break; + index = sig2arc[i]; + unsigned sigIndex = i - arc2sig[index]; + i = prevs[i]; + if (needCheckStartOpen && difficultBools[index]) + continue; + const CArcInfoEx &ai = op.codecs->Formats[index]; + + if (pos < ai.SignatureOffset) + continue; + + /* + if (openOnlyFullArc) + if (pos != ai.SignatureOffset) + continue; + */ + + const CByteBuffer &sig = ai.Signatures[sigIndex]; + + if (ppp + sig.Size() > availSize + || !TestSignature(buf + ppp, sig, sig.Size())) + continue; + // printf("\nSignature OK: %10S %8x %5d", (const wchar_t *)ai.Name, (int)pos, (int)(pos - prevPos)); + // prevPos = pos; + isDifficult = false; + } + + const CArcInfoEx &ai = op.codecs->Formats[index]; + + + if ((isDifficult && pos == 0) || ai.SignatureOffset == pos) + { + // we don't check same archive second time */ + if (skipFrontalFormat[index]) + continue; + } + + UInt64 startArcPos = pos; + if (!isDifficult) + { + if (pos < ai.SignatureOffset) + continue; + startArcPos = pos - ai.SignatureOffset; + /* + // we don't need the check for Z files + if (startArcPos < handlerSpec->GetLastEnd()) + continue; + */ + } + + if (ai.IsArcFunc && startArcPos >= bufPhyPos) + { + size_t offsetInBuf = (size_t)(startArcPos - bufPhyPos); + if (offsetInBuf < bytesInBuf) + { + UInt32 isArcRes = ai.IsArcFunc(byteBuffer + offsetInBuf, bytesInBuf - offsetInBuf); + if (isArcRes == k_IsArc_Res_NO) + continue; + if (isArcRes == k_IsArc_Res_NEED_MORE && endOfFile) + continue; + /* + if (isArcRes == k_IsArc_Res_YES_LOW_PROB) + { + // if (pos != ai.SignatureOffset) + continue; + } + */ + } + // printf("\nIsArc OK: %S", (const wchar_t *)ai.Name); + } + + /* + if (pos == 67109888) + pos = pos; + */ + PRF(printf("\npos = %9I64d : %S", pos, (const wchar_t *)ai.Name)); + + bool isMainFormat = isMainFormatArr[index]; + const COpenSpecFlags &specFlags = mode.GetSpec(isForced, isMainFormat, isUnknownExt); + + CMyComPtr<IInArchive> archive; + RINOK(PrepareToOpen(op, index, archive)); + if (!archive) + return E_FAIL; + + // OutputDebugStringW(ai.Name); + + UInt64 rem = fileSize - startArcPos; + + UInt64 arcStreamOffset = 0; + + if (ai.Flags_UseGlobalOffset()) + { + limitedStreamSpec->InitAndSeek(0, fileSize); + limitedStream->Seek(startArcPos, STREAM_SEEK_SET, NULL); + } + else + { + limitedStreamSpec->InitAndSeek(startArcPos, rem); + arcStreamOffset = startArcPos; + } + + UInt64 maxCheckStartPosition = 0; + + if (openCallback_Offset) + { + openCallback_Offset_Spec->Files = handlerSpec->_items.Size(); + openCallback_Offset_Spec->Offset = startArcPos; + } + + // HRESULT result = archive->Open(limitedStream, &maxCheckStartPosition, openCallback_Offset); + extractCallback_To_OpenCallback_Spec->Files = 0; + extractCallback_To_OpenCallback_Spec->Offset = startArcPos; + + HRESULT result = OpenArchiveSpec(archive, true, limitedStream, &maxCheckStartPosition, + useOffsetCallback ? (IArchiveOpenCallback *)openCallback_Offset : (IArchiveOpenCallback *)op.callback, + extractCallback_To_OpenCallback); + + RINOK(ReadBasicProps(archive, ai.Flags_UseGlobalOffset() ? 0 : startArcPos, result)); + + bool isOpen = false; + if (result == S_FALSE) + { + if (!mode.CanReturnParser) + { + if (formatIndex < 0 && ErrorInfo.IsArc_After_NonOpen()) + { + ErrorInfo.ErrorFormatIndex = index; + NonOpen_ErrorInfo = ErrorInfo; + // if archive was detected, we don't need additional open attempts + return S_FALSE; + } + continue; + } + if (!ErrorInfo.IsArc_After_NonOpen() || !PhySizeDefined || PhySize == 0) + continue; + } + else + { + isOpen = true; + RINOK(result); + PRF(printf(" OK ")); + } + + // fprintf(stderr, "\n %8X %S", startArcPos, Path); + // printf("\nOpen OK: %S", ai.Name); + + + NArchive::NParser::CParseItem pi; + pi.Offset = startArcPos; + + if (ai.Flags_UseGlobalOffset()) + pi.Offset = Offset; + else if (Offset != 0) + return E_FAIL; + UInt64 arcRem = FileSize - pi.Offset; + UInt64 phySize = arcRem; + bool phySizeDefined = PhySizeDefined; + if (phySizeDefined) + { + if (pi.Offset + PhySize > FileSize) + { + // ErrorInfo.ThereIsTail = true; + PhySize = FileSize - pi.Offset; + } + phySize = PhySize; + } + if (phySize == 0 || (UInt64)phySize > ((UInt64)1 << 63)) + return E_FAIL; + + /* + if (!ai.UseGlobalOffset) + { + if (phySize > arcRem) + { + ThereIsTail = true; + phySize = arcRem; + } + } + */ + + bool needScan = false; + + + if (isOpen && !phySizeDefined) + { + // it's for Z format + pi.LenIsUnknown = true; + needScan = true; + phySize = arcRem; + nextNeedCheckStartOpen = false; + } + + pi.Size = phySize; + /* + if (OkPhySize_Defined) + pi.OkSize = OkPhySize; + */ + pi.NormalizeOffset(); + // printf(" phySize = %8d", (unsigned)phySize); + + /* + if (needSkipFullArc) + if (pi.Offset == 0 && phySizeDefined && pi.Size >= fileSize) + continue; + */ + if (pi.Offset == 0 && !pi.LenIsUnknown && pi.Size >= FileSize) + { + // it's possible for dmg archives + if (!mode.CanReturnArc) + continue; + } + + if (mode.EachPos) + pos++; + else if (needScan) + { + pos++; + /* + if (!OkPhySize_Defined) + pos++; + else + pos = pi.Offset + pi.OkSize; + */ + } + else + pos = pi.Offset + pi.Size; + + + RINOK(ReadParseItemProps(archive, ai, pi)); + + if (pi.Offset < startArcPos && !mode.EachPos /* && phySizeDefined */) + { + /* It's for DMG format. + This code deletes all previous items that are included to current item */ + + while (!handlerSpec->_items.IsEmpty()) + { + { + const NArchive::NParser::CParseItem &back = handlerSpec->_items.Back(); + if (back.Offset < pi.Offset) + break; + if (back.Offset + back.Size > pi.Offset + pi.Size) + break; + } + handlerSpec->_items.DeleteBack(); + } + } + + + if (isOpen && mode.CanReturnArc && phySizeDefined) + { + // if (pi.Offset + pi.Size >= fileSize) + bool openCur = false; + + bool thereIsTail = ErrorInfo.ThereIsTail; + if (thereIsTail && mode.ZerosTailIsAllowed) + { + RINOK(CheckZerosTail(op, arcStreamOffset + Offset + PhySize)); + if (ErrorInfo.IgnoreTail) + thereIsTail = false; + } + + if (pi.Offset != 0) + { + if (!pi.IsNotArcType) + if (thereIsTail) + openCur = specFlags.CanReturnMid; + else + openCur = specFlags.CanReturnTail; + } + else + { + if (!thereIsTail) + openCur = true; + else + openCur = specFlags.CanReturnFrontal; + + + if (formatIndex >= -2) + openCur = true; + } + if (formatIndex < 0 && pi.IsSelfExe /* && mode.SkipSfxStub */) + openCur = false; + + // We open file as SFX, if there is front archive or first archive is "Self Executable" + if (!openCur && !pi.IsSelfExe && !thereIsTail && + (!pi.IsNotArcType || pi.Offset == 0)) + { + if (handlerSpec->_items.IsEmpty()) + { + if (specFlags.CanReturnTail) + openCur = true; + } + else if (handlerSpec->_items.Size() == 1) + { + if (handlerSpec->_items[0].IsSelfExe) + { + if (mode.SpecUnknownExt.CanReturnTail) + openCur = true; + } + } + } + + if (openCur) + { + InStream = op.stream; + Archive = archive; + FormatIndex = index; + ArcStreamOffset = arcStreamOffset; + return S_OK; + } + } + + /* + if (openOnlyFullArc) + { + ErrorInfo.ClearErrors(); + return S_FALSE; + } + */ + + pi.FormatIndex = index; + + // printf("\nAdd offset = %d", (int)pi.Offset); + handlerSpec->AddItem(pi); + wasOpen = true; + break; + } + // ---------- End of Open Loop for Current Pos ---------- + + if (!wasOpen) + pos++; + needCheckStartOpen = (nextNeedCheckStartOpen && wasOpen); } - #endif + // ---------- End of Main Scan Loop ---------- - // OutputDebugStringW(codecs->Formats[FormatIndex].Name); + /* + if (handlerSpec->_items.Size() == 1) + { + const NArchive::NParser::CParseItem &pi = handlerSpec->_items[0]; + if (pi.Size == fileSize && pi.Offset == 0) + { + Archive = archive; + FormatIndex2 = pi.FormatIndex; + return S_OK; + } + } + */ - HRESULT result; - if (stream) - result = archive->Open(stream, &kMaxCheckStartPosition, callback); - else + if (mode.CanReturnParser) { - CMyComPtr<IArchiveOpenSeq> openSeq; - archive.QueryInterface(IID_IArchiveOpenSeq, (void **)&openSeq); - if (!openSeq) - return E_NOTIMPL; - result = openSeq->OpenSeq(seqStream); + bool returnParser = (handlerSpec->_items.Size() == 1); // it's possible if fileSize was not correct at start of parsing + handlerSpec->AddUnknownItem(fileSize); + if (handlerSpec->_items.Size() == 0) + return S_FALSE; + if (returnParser || handlerSpec->_items.Size() != 1) + { + // return S_FALSE; + handlerSpec->_stream = op.stream; + Archive = handler; + ErrorInfo.ClearErrors(); + IsParseArc = true; + FormatIndex = -1; // It's parser + Offset = 0; + return S_OK; + } } + } - if (result == S_FALSE) - continue; - RINOK(result); + #endif + + if (!Archive) + return S_FALSE; + return S_OK; +} + +HRESULT CArc::OpenStream(const COpenOptions &op) +{ + RINOK(OpenStream2(op)); + // PrintNumber("op.formatIndex 3", op.formatIndex); + + if (Archive) + { + GetRawProps.Release(); + GetRootProps.Release(); + Archive->QueryInterface(IID_IArchiveGetRawProps, (void **)&GetRawProps); + Archive->QueryInterface(IID_IArchiveGetRootProps, (void **)&GetRootProps); + + RINOK(Archive_GetArcBoolProp(Archive, kpidIsTree, IsTree)); + RINOK(Archive_GetArcBoolProp(Archive, kpidIsDeleted, Ask_Deleted)); + RINOK(Archive_GetArcBoolProp(Archive, kpidIsAltStream, Ask_AltStream)); + RINOK(Archive_GetArcBoolProp(Archive, kpidIsAux, Ask_Aux)); + RINOK(Archive_GetArcBoolProp(Archive, kpidINode, Ask_INode)); + RINOK(Archive_GetArcBoolProp(Archive, kpidReadOnly, IsReadOnly)); + const UString fileName = ExtractFileNameFromPath(Path); + UString extension; { - NCOM::CPropVariant prop; - archive->GetArchiveProperty(kpidError, &prop); - if (prop.vt != VT_EMPTY) - ErrorMessage = (prop.vt == VT_BSTR) ? prop.bstrVal : L"Unknown error"; + int dotPos = fileName.ReverseFind_Dot(); + if (dotPos >= 0) + extension = fileName.Ptr(dotPos + 1); } - Archive = archive; - const CArcInfoEx &format = codecs->Formats[FormatIndex]; - if (format.Exts.Size() == 0) - DefaultName = GetDefaultName2(fileName, L"", L""); - else + DefaultName.Empty(); + if (FormatIndex >= 0) { - int subExtIndex = format.FindExtension(extension); - if (subExtIndex < 0) - subExtIndex = 0; - const CArcExtInfo &extInfo = format.Exts[subExtIndex]; - DefaultName = GetDefaultName2(fileName, extInfo.Ext, extInfo.AddExt); + const CArcInfoEx &ai = op.codecs->Formats[FormatIndex]; + if (ai.Exts.Size() == 0) + DefaultName = GetDefaultName2(fileName, UString(), UString()); + else + { + int subExtIndex = ai.FindExtension(extension); + if (subExtIndex < 0) + subExtIndex = 0; + const CArcExtInfo &extInfo = ai.Exts[subExtIndex]; + DefaultName = GetDefaultName2(fileName, extInfo.Ext, extInfo.AddExt); + } } - return S_OK; } - return S_FALSE; + + return S_OK; } -HRESULT CArc::OpenStreamOrFile( - CCodecs *codecs, - int formatIndex, - bool stdInMode, - IInStream *stream, - IArchiveOpenCallback *callback) +#ifdef _SFX + +#ifdef _WIN32 + static const char *k_ExeExt = ".exe"; + static const unsigned k_ExeExt_Len = 4; +#else + static const char *k_ExeExt = ""; + static const unsigned k_ExeExt_Len = 0; +#endif + +#endif + +HRESULT CArc::OpenStreamOrFile(COpenOptions &op) { CMyComPtr<IInStream> fileStream; CMyComPtr<ISequentialInStream> seqStream; - if (stdInMode) + CInFileStream *fileStreamSpec = NULL; + + if (op.stdInMode) + { seqStream = new CStdInFileStream; - else if (!stream) + op.seqStream = seqStream; + } + else if (!op.stream) { - CInFileStream *fileStreamSpec = new CInFileStream; + fileStreamSpec = new CInFileStream; fileStream = fileStreamSpec; + Path = filePath; if (!fileStreamSpec->Open(us2fs(Path))) + { return GetLastError(); - stream = fileStream; + } + op.stream = fileStream; + #ifdef _SFX + IgnoreSplit = true; + #endif } /* if (callback) { UInt64 fileSize; - RINOK(stream->Seek(0, STREAM_SEEK_END, &fileSize)); - RINOK(callback->SetTotal(NULL, &fileSize)) + RINOK(op.stream->Seek(0, STREAM_SEEK_END, &fileSize)); + RINOK(op.callback->SetTotal(NULL, &fileSize)) } */ - return OpenStream(codecs, formatIndex, stream, seqStream, callback); + HRESULT res = OpenStream(op); + IgnoreSplit = false; + + #ifdef _SFX + + if (res != S_FALSE + || !fileStreamSpec + || !op.callbackSpec + || NonOpen_ErrorInfo.IsArc_After_NonOpen()) + return res; + + { + if (filePath.Len() > k_ExeExt_Len + && StringsAreEqualNoCase_Ascii(filePath.RightPtr(k_ExeExt_Len), k_ExeExt)) + { + const UString path2 = filePath.Left(filePath.Len() - k_ExeExt_Len); + FOR_VECTOR (i, op.codecs->Formats) + { + const CArcInfoEx &ai = op.codecs->Formats[i]; + if (ai.IsSplit()) + continue; + UString path3 = path2; + path3 += L'.'; + path3 += ai.GetMainExt(); // "7z" for SFX. + Path = path3; + Path.AddAscii(".001"); + bool isOk = op.callbackSpec->SetSecondFileInfo(us2fs(Path)); + if (!isOk) + { + Path = path3; + isOk = op.callbackSpec->SetSecondFileInfo(us2fs(Path)); + } + if (isOk) + { + if (fileStreamSpec->Open(us2fs(Path))) + { + op.stream = fileStream; + NonOpen_ErrorInfo.ClearErrors_Full(); + if (OpenStream(op) == S_OK) + return S_OK; + } + } + } + } + } + + #endif + + return res; +} + +void CArchiveLink::KeepModeForNextOpen() +{ + for (unsigned i = Arcs.Size(); i != 0;) + { + i--; + CMyComPtr<IArchiveKeepModeForNextOpen> keep; + Arcs[i].Archive->QueryInterface(IID_IArchiveKeepModeForNextOpen, (void **)&keep); + if (keep) + keep->KeepModeForNextOpen(); + } } HRESULT CArchiveLink::Close() { - for (int i = Arcs.Size() - 1; i >= 0; i--) + for (unsigned i = Arcs.Size(); i != 0;) { - RINOK(Arcs[i].Archive->Close()); + i--; + RINOK(Arcs[i].Close()); } IsOpen = false; + // ErrorsText.Empty(); return S_OK; } void CArchiveLink::Release() { + // NonOpenErrorFormatIndex = -1; + NonOpen_ErrorInfo.ClearErrors(); + NonOpen_ArcPath.Empty(); while (!Arcs.IsEmpty()) Arcs.DeleteBack(); } -HRESULT CArchiveLink::Open( - CCodecs *codecs, - const CIntVector &formatIndices, - bool stdInMode, - IInStream *stream, - const UString &filePath, - IArchiveOpenCallback *callback) +/* +void CArchiveLink::Set_ErrorsText() +{ + FOR_VECTOR(i, Arcs) + { + const CArc &arc = Arcs[i]; + if (!arc.ErrorFlagsText.IsEmpty()) + { + if (!ErrorsText.IsEmpty()) + ErrorsText.Add_LF(); + ErrorsText += GetUnicodeString(arc.ErrorFlagsText); + } + if (!arc.ErrorMessage.IsEmpty()) + { + if (!ErrorsText.IsEmpty()) + ErrorsText.Add_LF(); + ErrorsText += arc.ErrorMessage; + } + + if (!arc.WarningMessage.IsEmpty()) + { + if (!ErrorsText.IsEmpty()) + ErrorsText.Add_LF(); + ErrorsText += arc.WarningMessage; + } + } +} +*/ + +HRESULT CArchiveLink::Open(COpenOptions &op) { Release(); - if (formatIndices.Size() >= 32) + if (op.types->Size() >= 32) return E_NOTIMPL; HRESULT resSpec; @@ -408,29 +3080,79 @@ HRESULT CArchiveLink::Open( for (;;) { resSpec = S_OK; - int formatIndex = -1; - if (formatIndices.Size() >= 1) + + op.openType = COpenType(); + if (op.types->Size() >= 1) { - if (Arcs.Size() >= formatIndices.Size()) - break; - formatIndex = formatIndices[formatIndices.Size() - Arcs.Size() - 1]; + COpenType latest; + if (Arcs.Size() < op.types->Size()) + latest = (*op.types)[op.types->Size() - Arcs.Size() - 1]; + else + { + latest = (*op.types)[0]; + if (!latest.Recursive) + break; + } + op.openType = latest; + } + else if (Arcs.Size() >= 32) + break; + + /* + op.formatIndex = -1; + if (op.types->Size() >= 1) + { + int latest; + if (Arcs.Size() < op.types->Size()) + latest = (*op.types)[op.types->Size() - Arcs.Size() - 1]; + else + { + latest = (*op.types)[0]; + if (latest != -2 && latest != -3) + break; + } + if (latest >= 0) + op.formatIndex = latest; + else if (latest == -1 || latest == -2) + { + // default + } + else if (latest == -3) + op.formatIndex = -2; + else + op.formatIndex = latest + 2; } else if (Arcs.Size() >= 32) break; + */ if (Arcs.IsEmpty()) { CArc arc; - arc.Path = filePath; + arc.filePath = op.filePath; + arc.Path = op.filePath; arc.SubfileIndex = (UInt32)(Int32)-1; - RINOK(arc.OpenStreamOrFile(codecs, formatIndex, stdInMode, stream, callback)); + HRESULT result = arc.OpenStreamOrFile(op); + if (result != S_OK) + { + if (result == S_FALSE) + { + NonOpen_ErrorInfo = arc.NonOpen_ErrorInfo; + // NonOpenErrorFormatIndex = arc.ErrorFormatIndex; + NonOpen_ArcPath = arc.Path; + } + return result; + } Arcs.Add(arc); continue; } + // PrintNumber("op.formatIndex 11", op.formatIndex); + const CArc &arc = Arcs.Back(); - resSpec = (formatIndices.Size() == 0 ? S_OK : E_NOTIMPL); + if (op.types->Size() > Arcs.Size()) + resSpec = E_NOTIMPL; UInt32 mainSubfile; { @@ -461,46 +3183,57 @@ HRESULT CArchiveLink::Open( CArc arc2; RINOK(arc.GetItemPath(mainSubfile, arc2.Path)); - - CMyComPtr<IArchiveOpenSetSubArchiveName> setSubArchiveName; - callback->QueryInterface(IID_IArchiveOpenSetSubArchiveName, (void **)&setSubArchiveName); - if (setSubArchiveName) - setSubArchiveName->SetSubArchiveName(arc2.Path); + + bool zerosTailIsAllowed; + RINOK(Archive_GetItemBoolProp(arc.Archive, mainSubfile, kpidZerosTailIsAllowed, zerosTailIsAllowed)); + + + if (op.callback) + { + CMyComPtr<IArchiveOpenSetSubArchiveName> setSubArchiveName; + op.callback->QueryInterface(IID_IArchiveOpenSetSubArchiveName, (void **)&setSubArchiveName); + if (setSubArchiveName) + setSubArchiveName->SetSubArchiveName(arc2.Path); + } arc2.SubfileIndex = mainSubfile; - HRESULT result = arc2.OpenStream(codecs, formatIndex, subStream, NULL, callback); - resSpec = (formatIndices.Size() == 0 ? S_OK : S_FALSE); + + // CIntVector incl; + CIntVector excl; + + COpenOptions op2; + #ifndef _SFX + op2.props = op.props; + #endif + op2.codecs = op.codecs; + // op2.types = &incl; + op2.openType = op.openType; + op2.openType.ZerosTailIsAllowed = zerosTailIsAllowed; + op2.excludedFormats = ! + op2.stdInMode = false; + op2.stream = subStream; + op2.filePath = arc2.Path; + op2.callback = op.callback; + op2.callbackSpec = op.callbackSpec; + + + HRESULT result = arc2.OpenStream(op2); + resSpec = (op.types->Size() == 0 ? S_OK : S_FALSE); if (result == S_FALSE) + { + NonOpen_ErrorInfo = arc2.ErrorInfo; + NonOpen_ArcPath = arc2.Path; break; + } RINOK(result); RINOK(arc.GetItemMTime(mainSubfile, arc2.MTime, arc2.MTimeDefined)); Arcs.Add(arc2); } IsOpen = !Arcs.IsEmpty(); - return S_OK; + return resSpec; } -static void SetCallback(const FString &filePath, - IOpenCallbackUI *callbackUI, - IArchiveOpenCallback *reOpenCallback, - CMyComPtr<IArchiveOpenCallback> &callback) -{ - COpenCallbackImp *openCallbackSpec = new COpenCallbackImp; - callback = openCallbackSpec; - openCallbackSpec->Callback = callbackUI; - openCallbackSpec->ReOpenCallback = reOpenCallback; - - FString dirPrefix, fileName; - NFile::NDirectory::GetFullPathAndSplit(filePath, dirPrefix, fileName); - openCallbackSpec->Init(dirPrefix, fileName); -} - -HRESULT CArchiveLink::Open2(CCodecs *codecs, - const CIntVector &formatIndices, - bool stdInMode, - IInStream *stream, - const UString &filePath, - IOpenCallbackUI *callbackUI) +HRESULT CArchiveLink::Open2(COpenOptions &op, IOpenCallbackUI *callbackUI) { VolumesSize = 0; COpenCallbackImp *openCallbackSpec = new COpenCallbackImp; @@ -508,41 +3241,266 @@ HRESULT CArchiveLink::Open2(CCodecs *codecs, openCallbackSpec->Callback = callbackUI; FString prefix, name; - if (!stream && !stdInMode) + + if (!op.stream && !op.stdInMode) { - NFile::NDirectory::GetFullPathAndSplit(us2fs(filePath), prefix, name); + NFile::NDir::GetFullPathAndSplit(us2fs(op.filePath), prefix, name); openCallbackSpec->Init(prefix, name); } else { - openCallbackSpec->SetSubArchiveName(filePath); + openCallbackSpec->SetSubArchiveName(op.filePath); } - RINOK(Open(codecs, formatIndices, stdInMode, stream, filePath, callback)); - VolumePaths.Add(fs2us(prefix + name)); - for (int i = 0; i < openCallbackSpec->FileNames.Size(); i++) - VolumePaths.Add(fs2us(prefix) + openCallbackSpec->FileNames[i]); - VolumesSize = openCallbackSpec->TotalSize; + op.callback = callback; + op.callbackSpec = openCallbackSpec; + + HRESULT res = Open(op); + + PasswordWasAsked = openCallbackSpec->PasswordWasAsked; + // Password = openCallbackSpec->Password; + + RINOK(res); + // VolumePaths.Add(fs2us(prefix + name)); + + FOR_VECTOR (i, openCallbackSpec->FileNames_WasUsed) + { + if (openCallbackSpec->FileNames_WasUsed[i]) + { + VolumePaths.Add(fs2us(prefix) + openCallbackSpec->FileNames[i]); + VolumesSize += openCallbackSpec->FileSizes[i]; + } + } + // VolumesSize = openCallbackSpec->TotalSize; return S_OK; } -HRESULT CArchiveLink::ReOpen(CCodecs *codecs, const UString &filePath, - IArchiveOpenCallback *callback) +HRESULT CArc::ReOpen(const COpenOptions &op) +{ + ErrorInfo.ClearErrors(); + ErrorInfo.ErrorFormatIndex = -1; + + UInt64 fileSize = 0; + if (op.stream) + { + RINOK(op.stream->Seek(0, STREAM_SEEK_END, &fileSize)); + RINOK(op.stream->Seek(0, STREAM_SEEK_SET, NULL)); + } + FileSize = fileSize; + + CMyComPtr<IInStream> stream2; + Int64 globalOffset = GetGlobalOffset(); + if (globalOffset <= 0) + stream2 = op.stream; + else + { + CTailInStream *tailStreamSpec = new CTailInStream; + stream2 = tailStreamSpec; + tailStreamSpec->Stream = op.stream; + tailStreamSpec->Offset = globalOffset; + tailStreamSpec->Init(); + RINOK(tailStreamSpec->SeekToStart()); + } + + // There are archives with embedded STUBs (like ZIP), so we must support signature scanning + // But for another archives we can use 0 here. So the code can be fixed !!! + UInt64 maxStartPosition = kMaxCheckStartPosition; + HRESULT res = Archive->Open(stream2, &maxStartPosition, op.callback); + + if (res == S_OK) + { + RINOK(ReadBasicProps(Archive, globalOffset, res)); + ArcStreamOffset = globalOffset; + if (ArcStreamOffset != 0) + InStream = op.stream; + } + return res; +} + +HRESULT CArchiveLink::Open3(COpenOptions &op, IOpenCallbackUI *callbackUI) +{ + HRESULT res = Open2(op, callbackUI); + if (callbackUI) + { + RINOK(callbackUI->Open_Finished()); + } + return res; +} + +HRESULT CArchiveLink::ReOpen(COpenOptions &op) { if (Arcs.Size() > 1) return E_NOTIMPL; - if (Arcs.Size() == 0) - return Open2(codecs, CIntVector(), false, NULL, filePath, 0); + CObjectVector<COpenType> inc; + CIntVector excl; + + op.types = &inc; + op.excludedFormats = ! + op.stdInMode = false; + op.stream = NULL; + if (Arcs.Size() == 0) // ??? + return Open2(op, NULL); + + COpenCallbackImp *openCallbackSpec = new COpenCallbackImp; + CMyComPtr<IArchiveOpenCallback> openCallbackNew = openCallbackSpec; + + openCallbackSpec->Callback = NULL; + openCallbackSpec->ReOpenCallback = op.callback; + { + FString dirPrefix, fileName; + NFile::NDir::GetFullPathAndSplit(us2fs(op.filePath), dirPrefix, fileName); + openCallbackSpec->Init(dirPrefix, fileName); + } - CMyComPtr<IArchiveOpenCallback> openCallbackNew; - SetCallback(us2fs(filePath), NULL, callback, openCallbackNew); CInFileStream *fileStreamSpec = new CInFileStream; CMyComPtr<IInStream> stream(fileStreamSpec); - if (!fileStreamSpec->Open(us2fs(filePath))) + if (!fileStreamSpec->Open(us2fs(op.filePath))) return GetLastError(); - HRESULT res = GetArchive()->Open(stream, &kMaxCheckStartPosition, callback); + op.stream = stream; + + CArc &arc = Arcs[0]; + HRESULT res = arc.ReOpen(op); + + PasswordWasAsked = openCallbackSpec->PasswordWasAsked; + // Password = openCallbackSpec->Password; + IsOpen = (res == S_OK); return res; } + +#ifndef _SFX + +bool ParseComplexSize(const wchar_t *s, UInt64 &result) +{ + result = 0; + const wchar_t *end; + UInt64 number = ConvertStringToUInt64(s, &end); + if (end == s) + return false; + if (*end == 0) + { + result = number; + return true; + } + if (end[1] != 0) + return false; + unsigned numBits; + switch (MyCharLower_Ascii(*end)) + { + case 'b': result = number; return true; + case 'k': numBits = 10; break; + case 'm': numBits = 20; break; + case 'g': numBits = 30; break; + case 't': numBits = 40; break; + default: return false; + } + if (number >= ((UInt64)1 << (64 - numBits))) + return false; + result = number << numBits; + return true; +} + +static bool ParseTypeParams(const UString &s, COpenType &type) +{ + if (s[0] == 0) + return true; + if (s[1] == 0) + { + switch ((unsigned)(Byte)s[0]) + { + case 'e': type.EachPos = true; return true; + case 'a': type.CanReturnArc = true; return true; + case 'r': type.Recursive = true; return true; + } + return false; + } + if (s[0] == 's') + { + UInt64 result; + if (!ParseComplexSize(s.Ptr(1), result)) + return false; + type.MaxStartOffset = result; + type.MaxStartOffset_Defined = true; + return true; + } + + return false; +} + +bool ParseType(CCodecs &codecs, const UString &s, COpenType &type) +{ + int pos2 = s.Find(L':'); + UString name; + if (pos2 < 0) + { + name = s; + pos2 = s.Len(); + } + else + { + name = s.Left(pos2); + pos2++; + } + + int index = codecs.FindFormatForArchiveType(name); + type.Recursive = false; + + if (index < 0) + { + if (name[0] == '*') + { + if (name[1] != 0) + return false; + } + else if (name[0] == '#') + { + if (name[1] != 0) + return false; + type.CanReturnArc = false; + type.CanReturnParser = true; + } + else + return false; + } + + type.FormatIndex = index; + + for (unsigned i = pos2; i < s.Len();) + { + int next = s.Find(L':', i); + if (next < 0) + next = s.Len(); + UString name = s.Mid(i, next - i); + if (name.IsEmpty()) + return false; + if (!ParseTypeParams(name, type)) + return false; + i = next + 1; + } + + return true; +} + +bool ParseOpenTypes(CCodecs &codecs, const UString &s, CObjectVector<COpenType> &types) +{ + types.Clear(); + for (unsigned pos = 0; pos < s.Len();) + { + int pos2 = s.Find(L'.', pos); + if (pos2 < 0) + pos2 = s.Len(); + UString name = s.Mid(pos, pos2 - pos); + if (name.IsEmpty()) + return false; + COpenType type; + if (!ParseType(codecs, name, type)) + return false; + types.Add(type); + pos = pos2 + 1; + } + return true; +} + +#endif diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/OpenArchive.h b/3rdparty/lzma/CPP/7zip/UI/Common/OpenArchive.h index 25f739e3035..10514ec4000 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/OpenArchive.h +++ b/3rdparty/lzma/CPP/7zip/UI/Common/OpenArchive.h @@ -3,50 +3,364 @@ #ifndef __OPEN_ARCHIVE_H #define __OPEN_ARCHIVE_H -#include "Common/MyString.h" - -#include "Windows/FileFind.h" - -#include "../../Archive/IArchive.h" +#include "../../../Windows/PropVariant.h" #include "ArchiveOpenCallback.h" #include "LoadCodecs.h" +#include "Property.h" + +#ifndef _SFX + +#define SUPPORT_ALT_STREAMS + +#endif + +HRESULT Archive_GetItemBoolProp(IInArchive *arc, UInt32 index, PROPID propID, bool &result) throw(); +HRESULT Archive_IsItem_Dir(IInArchive *arc, UInt32 index, bool &result) throw(); +HRESULT Archive_IsItem_Aux(IInArchive *arc, UInt32 index, bool &result) throw(); +HRESULT Archive_IsItem_AltStream(IInArchive *arc, UInt32 index, bool &result) throw(); +HRESULT Archive_IsItem_Deleted(IInArchive *arc, UInt32 index, bool &deleted) throw(); + +#ifdef SUPPORT_ALT_STREAMS +int FindAltStreamColon_in_Path(const wchar_t *path); +#endif + +/* +struct COptionalOpenProperties +{ + UString FormatName; + CObjectVector<CProperty> Props; +}; +*/ + +#ifdef _SFX +#define OPEN_PROPS_DECL +#else +#define OPEN_PROPS_DECL const CObjectVector<CProperty> *props; +// #define OPEN_PROPS_DECL , const CObjectVector<COptionalOpenProperties> *props +#endif + +struct COpenSpecFlags +{ + // bool CanReturnFull; + bool CanReturnFrontal; + bool CanReturnTail; + bool CanReturnMid; + + bool CanReturn_NonStart() const { return CanReturnTail || CanReturnMid; } + + COpenSpecFlags(): + // CanReturnFull(true), + CanReturnFrontal(false), + CanReturnTail(false), + CanReturnMid(false) + {} +}; + +struct COpenType +{ + int FormatIndex; + + COpenSpecFlags SpecForcedType; + COpenSpecFlags SpecMainType; + COpenSpecFlags SpecWrongExt; + COpenSpecFlags SpecUnknownExt; -HRESULT GetArchiveItemBoolProp(IInArchive *archive, UInt32 index, PROPID propID, bool &result); -HRESULT IsArchiveItemFolder(IInArchive *archive, UInt32 index, bool &result); + bool Recursive; -struct CArc + bool CanReturnArc; + bool CanReturnParser; + bool EachPos; + + // bool SkipSfxStub; + // bool ExeAsUnknown; + + bool ZerosTailIsAllowed; + + bool MaxStartOffset_Defined; + UInt64 MaxStartOffset; + + const COpenSpecFlags &GetSpec(bool isForced, bool isMain, bool isUnknown) const + { + return isForced ? SpecForcedType : (isMain ? SpecMainType : (isUnknown ? SpecUnknownExt : SpecWrongExt)); + } + + COpenType(): + FormatIndex(-1), + Recursive(true), + EachPos(false), + CanReturnArc(true), + CanReturnParser(false), + // SkipSfxStub(true), + // ExeAsUnknown(true), + ZerosTailIsAllowed(false), + MaxStartOffset_Defined(false), + MaxStartOffset(0) + { + SpecForcedType.CanReturnFrontal = true; + SpecForcedType.CanReturnTail = true; + SpecForcedType.CanReturnMid = true; + + SpecMainType.CanReturnFrontal = true; + + SpecUnknownExt.CanReturnTail = true; // for sfx + SpecUnknownExt.CanReturnMid = true; + SpecUnknownExt.CanReturnFrontal = true; // for alt streams of sfx with pad + + // ZerosTailIsAllowed = true; + } +}; + +struct COpenOptions { + CCodecs *codecs; + COpenType openType; + const CObjectVector<COpenType> *types; + const CIntVector *excludedFormats; + + IInStream *stream; + ISequentialInStream *seqStream; + IArchiveOpenCallback *callback; + COpenCallbackImp *callbackSpec; + OPEN_PROPS_DECL + // bool openOnlySpecifiedByExtension, + + bool stdInMode; + UString filePath; + + COpenOptions(): + codecs(NULL), + types(NULL), + excludedFormats(NULL), + stream(NULL), + seqStream(NULL), + callback(NULL), + callbackSpec(NULL), + stdInMode(false) + {} + +}; + +UInt32 GetOpenArcErrorFlags(const NWindows::NCOM::CPropVariant &prop, bool *isDefinedProp = NULL); + +struct CArcErrorInfo +{ + bool ThereIsTail; + bool UnexpecedEnd; + bool IgnoreTail; // all are zeros + // bool NonZerosTail; + bool ErrorFlags_Defined; + UInt32 ErrorFlags; + UInt32 WarningFlags; + int ErrorFormatIndex; // - 1 means no Error. + // if FormatIndex == ErrorFormatIndex, the archive is open with offset + UInt64 TailSize; + + /* if CArc is Open OK with some format: + - ErrorFormatIndex shows error format index, if extension is incorrect + - other variables show message and warnings of archive that is open */ + + UString ErrorMessage; + UString WarningMessage; + + // call IsArc_After_NonOpen only if Open returns S_FALSE + bool IsArc_After_NonOpen() const + { + return (ErrorFlags_Defined && (ErrorFlags & kpv_ErrorFlags_IsNotArc) == 0); + } + + + CArcErrorInfo(): + ThereIsTail(false), + UnexpecedEnd(false), + IgnoreTail(false), + // NonZerosTail(false), + ErrorFlags_Defined(false), + ErrorFlags(0), + WarningFlags(0), + ErrorFormatIndex(-1), + TailSize(0) + {} + + void ClearErrors(); + + void ClearErrors_Full() + { + ErrorFormatIndex = -1; + ClearErrors(); + } + + bool IsThereErrorOrWarning() const + { + return ErrorFlags != 0 + || WarningFlags != 0 + || NeedTailWarning() + || UnexpecedEnd + || !ErrorMessage.IsEmpty() + || !WarningMessage.IsEmpty(); + } + + bool AreThereErrors() const { return ErrorFlags != 0 || UnexpecedEnd; } + bool AreThereWarnings() const { return WarningFlags != 0 || NeedTailWarning(); } + + bool NeedTailWarning() const { return !IgnoreTail && ThereIsTail; } + + UInt32 GetWarningFlags() const + { + UInt32 a = WarningFlags; + if (NeedTailWarning() && (ErrorFlags & kpv_ErrorFlags_DataAfterEnd) == 0) + a |= kpv_ErrorFlags_DataAfterEnd; + return a; + } + + UInt32 GetErrorFlags() const + { + UInt32 a = ErrorFlags; + if (UnexpecedEnd) + a |= kpv_ErrorFlags_UnexpectedEnd; + return a; + } +}; + +struct CReadArcItem +{ + UString Path; // Path from root (including alt stream name, if alt stream) + UStringVector PathParts; // without altStream name, path from root or from _baseParentFolder, if _use_baseParentFolder_mode + + #ifdef SUPPORT_ALT_STREAMS + UString MainPath; + /* MainPath = Path for non-AltStream, + MainPath = Path of parent, if there is parent for AltStream. */ + UString AltStreamName; + bool IsAltStream; + bool WriteToAltStreamIfColon; + #endif + + bool IsDir; + bool MainIsDir; + UInt32 ParentIndex; // use it, if IsAltStream + + #ifndef _SFX + bool _use_baseParentFolder_mode; + int _baseParentFolder; + #endif + + CReadArcItem() + { + #ifdef SUPPORT_ALT_STREAMS + WriteToAltStreamIfColon = false; + #endif + + #ifndef _SFX + _use_baseParentFolder_mode = false; + _baseParentFolder = -1; + #endif + } +}; + +class CArc +{ + HRESULT PrepareToOpen(const COpenOptions &op, unsigned formatIndex, CMyComPtr<IInArchive> &archive); + HRESULT CheckZerosTail(const COpenOptions &op, UInt64 offset); + HRESULT OpenStream2(const COpenOptions &options); + + #ifndef _SFX + // parts.Back() can contain alt stream name "nams:AltName" + HRESULT GetItemPathToParent(UInt32 index, UInt32 parent, UStringVector &parts) const; + #endif + +public: CMyComPtr<IInArchive> Archive; + CMyComPtr<IInStream> InStream; + // we use InStream in 2 cases (ArcStreamOffset != 0): + // 1) if we use additional cache stream + // 2) we reopen sfx archive with CTailInStream + + CMyComPtr<IArchiveGetRawProps> GetRawProps; + CMyComPtr<IArchiveGetRootProps> GetRootProps; + + CArcErrorInfo ErrorInfo; // for OK archives + CArcErrorInfo NonOpen_ErrorInfo; // ErrorInfo for mainArchive (false OPEN) + UString Path; + UString filePath; UString DefaultName; - int FormatIndex; + int FormatIndex; // - 1 means Parser. int SubfileIndex; FILETIME MTime; bool MTimeDefined; - UString ErrorMessage; + + Int64 Offset; // it's offset of start of archive inside stream that is open by Archive Handler + UInt64 PhySize; + // UInt64 OkPhySize; + bool PhySizeDefined; + // bool OkPhySize_Defined; + UInt64 FileSize; + UInt64 AvailPhySize; // PhySize, but it's reduced if exceed end of file + // bool offsetDefined; + + UInt64 GetEstmatedPhySize() const { return PhySizeDefined ? PhySize : FileSize; } + + UInt64 ArcStreamOffset; // offset of stream that is open by Archive Handler + Int64 GetGlobalOffset() const { return ArcStreamOffset + Offset; } // it's global offset of archive + + // AString ErrorFlagsText; + + bool IsParseArc; + + bool IsTree; + bool IsReadOnly; + + bool Ask_Deleted; + bool Ask_AltStream; + bool Ask_Aux; + bool Ask_INode; + + bool IgnoreSplit; // don't try split handler + + // void Set_ErrorFlagsText(); + + CArc(): + MTimeDefined(false), + IsTree(false), + IsReadOnly(false), + Ask_Deleted(false), + Ask_AltStream(false), + Ask_Aux(false), + Ask_INode(false), + IgnoreSplit(false) + {} + + HRESULT ReadBasicProps(IInArchive *archive, UInt64 startPos, HRESULT openRes); + + // ~CArc(); - CArc(): MTimeDefined(false) {} + HRESULT Close() + { + InStream.Release(); + return Archive->Close(); + } HRESULT GetItemPath(UInt32 index, UString &result) const; + HRESULT GetDefaultItemPath(UInt32 index, UString &result) const; + + // GetItemPath2 adds [DELETED] dir prefix for deleted items. + HRESULT GetItemPath2(UInt32 index, UString &result) const; + + HRESULT GetItem(UInt32 index, CReadArcItem &item) const; + HRESULT GetItemSize(UInt32 index, UInt64 &size, bool &defined) const; HRESULT GetItemMTime(UInt32 index, FILETIME &ft, bool &defined) const; HRESULT IsItemAnti(UInt32 index, bool &result) const - { return GetArchiveItemBoolProp(Archive, index, kpidIsAnti, result); } - - HRESULT OpenStream( - CCodecs *codecs, - int formatIndex, - IInStream *stream, - ISequentialInStream *seqStream, - IArchiveOpenCallback *callback); - - HRESULT OpenStreamOrFile( - CCodecs *codecs, - int formatIndex, - bool stdInMode, - IInStream *stream, - IArchiveOpenCallback *callback); + { return Archive_GetItemBoolProp(Archive, index, kpidIsAnti, result); } + + + HRESULT OpenStream(const COpenOptions &options); + HRESULT OpenStreamOrFile(COpenOptions &options); + + HRESULT ReOpen(const COpenOptions &options); + + HRESULT CreateNewTailStream(CMyComPtr<IInStream> &stream); }; struct CArchiveLink @@ -56,33 +370,40 @@ struct CArchiveLink UInt64 VolumesSize; bool IsOpen; - CArchiveLink(): VolumesSize(0), IsOpen(false) {} + bool PasswordWasAsked; + // UString Password; + + // int NonOpenErrorFormatIndex; // - 1 means no Error. + UString NonOpen_ArcPath; + + CArcErrorInfo NonOpen_ErrorInfo; + + // UString ErrorsText; + // void Set_ErrorsText(); + + CArchiveLink(): + VolumesSize(0), + IsOpen(false), + PasswordWasAsked(false) + {} + + void KeepModeForNextOpen(); HRESULT Close(); void Release(); ~CArchiveLink() { Release(); } + const CArc *GetArc() const { return &Arcs.Back(); } IInArchive *GetArchive() const { return Arcs.Back().Archive; } + IArchiveGetRawProps *GetArchiveGetRawProps() const { return Arcs.Back().GetRawProps; } + IArchiveGetRootProps *GetArchiveGetRootProps() const { return Arcs.Back().GetRootProps; } - HRESULT Open( - CCodecs *codecs, - const CIntVector &formatIndices, - bool stdInMode, - IInStream *stream, - const UString &filePath, - IArchiveOpenCallback *callback); - - HRESULT Open2( - CCodecs *codecs, - const CIntVector &formatIndices, - bool stdInMode, - IInStream *stream, - const UString &filePath, - IOpenCallbackUI *callbackUI); - - HRESULT ReOpen( - CCodecs *codecs, - const UString &filePath, - IArchiveOpenCallback *callback); + HRESULT Open(COpenOptions &options); + HRESULT Open2(COpenOptions &options, IOpenCallbackUI *callbackUI); + HRESULT Open3(COpenOptions &options, IOpenCallbackUI *callbackUI); + + HRESULT ReOpen(COpenOptions &options); }; +bool ParseOpenTypes(CCodecs &codecs, const UString &s, CObjectVector<COpenType> &types); + #endif diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/PropIDUtils.cpp b/3rdparty/lzma/CPP/7zip/UI/Common/PropIDUtils.cpp index dacaca5f988..dccc08b171e 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/PropIDUtils.cpp +++ b/3rdparty/lzma/CPP/7zip/UI/Common/PropIDUtils.cpp @@ -2,33 +2,28 @@ #include "StdAfx.h" -#include "Common/IntToString.h" +#include "../../../../C/CpuArch.h" -#include "Windows/FileFind.h" -#include "Windows/PropVariantConversions.h" +#include "../../../Common/IntToString.h" +#include "../../../Common/StringConvert.h" + +#include "../../../Windows/FileIO.h" +#include "../../../Windows/PropVariantConv.h" #include "../../PropID.h" #include "PropIDUtils.h" -using namespace NWindows; +#define Get16(x) GetUi16(x) +#define Get32(x) GetUi32(x) -void ConvertUInt32ToHex(UInt32 value, wchar_t *s) -{ - for (int i = 0; i < 8; i++) - { - int t = value & 0xF; - value >>= 4; - s[7 - i] = (wchar_t)((t < 10) ? (L'0' + t) : (L'A' + (t - 10))); - } - s[8] = L'\0'; -} +using namespace NWindows; -static const char g_WinAttrib[17] = "RHS8DAdNTsrCOnE_"; +static const char g_WinAttribChars[16 + 1] = "RHS8DAdNTsLCOnE_"; /* 0 READONLY 1 HIDDEN -3 SYSTEM +2 SYSTEM 4 DIRECTORY 5 ARCHIVE @@ -46,75 +41,534 @@ static const char g_WinAttrib[17] = "RHS8DAdNTsrCOnE_"; */ static const char kPosixTypes[16] = { '0', 'p', 'c', '3', 'd', '5', 'b', '7', '-', '9', 'l', 'B', 's', 'D', 'E', 'F' }; -#define MY_ATTR_CHAR(a, n, c) ((a )& (1 << (n))) ? c : L'-'; +#define MY_ATTR_CHAR(a, n, c) ((a) & (1 << (n))) ? c : '-'; -UString ConvertPropertyToString(const PROPVARIANT &prop, PROPID propID, bool full) +static void ConvertPosixAttribToString(char *s, UInt32 a) throw() { - switch(propID) + s[0] = kPosixTypes[(a >> 12) & 0xF]; + for (int i = 6; i >= 0; i -= 3) + { + s[7 - i] = MY_ATTR_CHAR(a, i + 2, 'r'); + s[8 - i] = MY_ATTR_CHAR(a, i + 1, 'w'); + s[9 - i] = MY_ATTR_CHAR(a, i + 0, 'x'); + } + if ((a & 0x800) != 0) s[3] = ((a & (1 << 6)) ? 's' : 'S'); + if ((a & 0x400) != 0) s[6] = ((a & (1 << 3)) ? 's' : 'S'); + if ((a & 0x200) != 0) s[9] = ((a & (1 << 0)) ? 't' : 'T'); + s[10] = 0; + + a &= ~(UInt32)0xFFFF; + if (a != 0) + { + s[10] = ' '; + ConvertUInt32ToHex8Digits(a, s + 11); + } +} + +void ConvertWinAttribToString(char *s, UInt32 wa) throw() +{ + for (int i = 0; i < 16; i++) + if ((wa & (1 << i)) && i != 7) + *s++ = g_WinAttribChars[i]; + *s = 0; + + // we support p7zip trick that stores posix attributes in high 16 bits, and 0x8000 flag + // we also support ZIP archives created in Unix, that store posix attributes in high 16 bits without 0x8000 flag + + // if (wa & 0x8000) + if ((wa >> 16) != 0) + { + *s++ = ' '; + ConvertPosixAttribToString(s, wa >> 16); + } +} + +void ConvertPropertyToShortString(char *dest, const PROPVARIANT &prop, PROPID propID, bool full) throw() +{ + *dest = 0; + + if (prop.vt == VT_FILETIME) + { + FILETIME localFileTime; + if ((prop.filetime.dwHighDateTime == 0 && + prop.filetime.dwLowDateTime == 0) || + !::FileTimeToLocalFileTime(&prop.filetime, &localFileTime)) + return; + ConvertFileTimeToString(localFileTime, dest, true, full); + return; + } + + switch (propID) { - case kpidCTime: - case kpidATime: - case kpidMTime: - { - if (prop.vt != VT_FILETIME) - break; - FILETIME localFileTime; - if ((prop.filetime.dwHighDateTime == 0 && - prop.filetime.dwLowDateTime == 0) || - !::FileTimeToLocalFileTime(&prop.filetime, &localFileTime)) - return UString(); - return ConvertFileTimeToString(localFileTime, true, full); - } case kpidCRC: { if (prop.vt != VT_UI4) break; - wchar_t temp[12]; - ConvertUInt32ToHex(prop.ulVal, temp); - return temp; + ConvertUInt32ToHex8Digits(prop.ulVal, dest); + return; } case kpidAttrib: { if (prop.vt != VT_UI4) break; UInt32 a = prop.ulVal; - wchar_t sz[32]; - int pos = 0; - for (int i = 0; i < 16; i++) - if (a & (1 << i) && i != 7) - sz[pos++] = g_WinAttrib[i]; - sz[pos] = '\0'; - return sz; + + /* + if ((a & 0x8000) && (a & 0x7FFF) == 0) + ConvertPosixAttribToString(dest, a >> 16); + else + */ + ConvertWinAttribToString(dest, a); + return; } case kpidPosixAttrib: { if (prop.vt != VT_UI4) break; - UString res; - UInt32 a = prop.ulVal; - wchar_t temp[16]; + ConvertPosixAttribToString(dest, prop.ulVal); + return; + } + case kpidINode: + { + if (prop.vt != VT_UI8) + break; + ConvertUInt32ToString((UInt32)(prop.uhVal.QuadPart >> 48), dest); + dest += strlen(dest); + *dest++ = '-'; + UInt64 low = prop.uhVal.QuadPart & (((UInt64)1 << 48) - 1); + ConvertUInt64ToString(low, dest); + return; + } + case kpidVa: + { + UInt64 v = 0; + if (prop.vt == VT_UI4) + v = prop.ulVal; + else if (prop.vt == VT_UI8) + v = (UInt64)prop.uhVal.QuadPart; + else + break; + dest[0] = '0'; + dest[1] = 'x'; + ConvertUInt64ToHex(v, dest + 2); + return; + } + } + + ConvertPropVariantToShortString(prop, dest); +} - temp[0] = kPosixTypes[(a >> 12) & 0xF]; - for (int i = 6; i >= 0; i -= 3) - { - temp[7 - i] = MY_ATTR_CHAR(a, i + 2, L'r'); - temp[8 - i] = MY_ATTR_CHAR(a, i + 1, L'w'); - temp[9 - i] = MY_ATTR_CHAR(a, i + 0, L'x'); - } - if ((a & 0x800) != 0) temp[3] = ((a & (1 << 6)) ? 's' : 'S'); - if ((a & 0x400) != 0) temp[6] = ((a & (1 << 3)) ? 's' : 'S'); - if ((a & 0x200) != 0) temp[9] = ((a & (1 << 0)) ? 't' : 'T'); - temp[10] = 0; - res = temp; - - a &= ~(UInt32)0xFFFF; - if (a != 0) +void ConvertPropertyToString(UString &dest, const PROPVARIANT &prop, PROPID propID, bool full) +{ + if (prop.vt == VT_BSTR) + { + dest.SetFromBstr(prop.bstrVal); + return; + } + char temp[64]; + ConvertPropertyToShortString(temp, prop, propID, full); + dest.SetFromAscii(temp); +} + +static inline unsigned GetHex(unsigned v) +{ + return (v < 10) ? ('0' + v) : ('A' + (v - 10)); +} + +#ifndef _SFX + +static inline void AddHexToString(AString &res, unsigned v) +{ + res += (char)GetHex(v >> 4); + res += (char)GetHex(v & 0xF); + res += ' '; +} + +/* +static AString Data_To_Hex(const Byte *data, size_t size) +{ + AString s; + for (size_t i = 0; i < size; i++) + AddHexToString(s, data[i]); + return s; +} +*/ + +static const char * const sidNames[] = +{ + "0" + , "Dialup" + , "Network" + , "Batch" + , "Interactive" + , "Logon" // S-1-5-5-X-Y + , "Service" + , "Anonymous" + , "Proxy" + , "EnterpriseDC" + , "Self" + , "AuthenticatedUsers" + , "RestrictedCode" + , "TerminalServer" + , "RemoteInteractiveLogon" + , "ThisOrganization" + , "16" + , "IUserIIS" + , "LocalSystem" + , "LocalService" + , "NetworkService" + , "Domains" +}; + +struct CSecID2Name +{ + UInt32 n; + const char *sz; +}; + +static const CSecID2Name sid_32_Names[] = +{ + { 544, "Administrators" }, + { 545, "Users" }, + { 546, "Guests" }, + { 547, "PowerUsers" }, + { 548, "AccountOperators" }, + { 549, "ServerOperators" }, + { 550, "PrintOperators" }, + { 551, "BackupOperators" }, + { 552, "Replicators" }, + { 553, "Backup Operators" }, + { 554, "PreWindows2000CompatibleAccess" }, + { 555, "RemoteDesktopUsers" }, + { 556, "NetworkConfigurationOperators" }, + { 557, "IncomingForestTrustBuilders" }, + { 558, "PerformanceMonitorUsers" }, + { 559, "PerformanceLogUsers" }, + { 560, "WindowsAuthorizationAccessGroup" }, + { 561, "TerminalServerLicenseServers" }, + { 562, "DistributedCOMUsers" }, + { 569, "CryptographicOperators" }, + { 573, "EventLogReaders" }, + { 574, "CertificateServiceDCOMAccess" } +}; + +static const CSecID2Name sid_21_Names[] = +{ + { 500, "Administrator" }, + { 501, "Guest" }, + { 502, "KRBTGT" }, + { 512, "DomainAdmins" }, + { 513, "DomainUsers" }, + { 515, "DomainComputers" }, + { 516, "DomainControllers" }, + { 517, "CertPublishers" }, + { 518, "SchemaAdmins" }, + { 519, "EnterpriseAdmins" }, + { 520, "GroupPolicyCreatorOwners" }, + { 553, "RASandIASServers" }, + { 553, "RASandIASServers" }, + { 571, "AllowedRODCPasswordReplicationGroup" }, + { 572, "DeniedRODCPasswordReplicationGroup" } +}; + +struct CServicesToName +{ + UInt32 n[5]; + const char *sz; +}; + +static const CServicesToName services_to_name[] = +{ + { { 0x38FB89B5, 0xCBC28419, 0x6D236C5C, 0x6E770057, 0x876402C0 } , "TrustedInstaller" } +}; + +static void ParseSid(AString &s, const Byte *p, UInt32 lim, UInt32 &sidSize) +{ + sidSize = 0; + if (lim < 8) + { + s += "ERROR"; + return; + } + UInt32 rev = p[0]; + if (rev != 1) + { + s += "UNSUPPORTED"; + return; + } + UInt32 num = p[1]; + if (8 + num * 4 > lim) + { + s += "ERROR"; + return; + } + sidSize = 8 + num * 4; + UInt32 authority = GetBe32(p + 4); + + if (p[2] == 0 && p[3] == 0 && authority == 5 && num >= 1) + { + UInt32 v0 = Get32(p + 8); + if (v0 < ARRAY_SIZE(sidNames)) + { + s += sidNames[v0]; + return; + } + if (v0 == 32 && num == 2) + { + UInt32 v1 = Get32(p + 12); + for (unsigned i = 0; i < ARRAY_SIZE(sid_32_Names); i++) + if (sid_32_Names[i].n == v1) + { + s += sid_32_Names[i].sz; + return; + } + } + if (v0 == 21 && num == 5) + { + UInt32 v4 = Get32(p + 8 + 4 * 4); + for (unsigned i = 0; i < ARRAY_SIZE(sid_21_Names); i++) + if (sid_21_Names[i].n == v4) + { + s += sid_21_Names[i].sz; + return; + } + } + if (v0 == 80 && num == 6) + { + for (unsigned i = 0; i < ARRAY_SIZE(services_to_name); i++) { - ConvertUInt32ToHex(a, temp); - res = UString(temp) + L' ' + res; + const CServicesToName &sn = services_to_name[i]; + int j; + for (j = 0; j < 5 && sn.n[j] == Get32(p + 8 + 4 + j * 4); j++); + if (j == 5) + { + s += sn.sz; + return; + } } - return res; } } - return ConvertPropVariantToString(prop); + + char sz[16]; + s += "S-1-"; + if (p[2] == 0 && p[3] == 0) + { + ConvertUInt32ToString(authority, sz); + s += sz; + } + else + { + s += "0x"; + for (int i = 2; i < 8; i++) + AddHexToString(s, p[i]); + } + for (UInt32 i = 0; i < num; i++) + { + s += '-'; + ConvertUInt32ToString(Get32(p + 8 + i * 4), sz); + s += sz; + } } + +static void ParseOwner(AString &s, const Byte *p, UInt32 size, UInt32 pos) +{ + if (pos > size) + { + s += "ERROR"; + return; + } + UInt32 sidSize = 0; + ParseSid(s, p + pos, size - pos, sidSize); +} + +static void AddUInt32ToString(AString &s, UInt32 val) +{ + char sz[16]; + ConvertUInt32ToString(val, sz); + s += sz; +} + +static void ParseAcl(AString &s, const Byte *p, UInt32 size, const char *strName, UInt32 flags, UInt32 offset) +{ + UInt32 control = Get16(p + 2); + if ((flags & control) == 0) + return; + UInt32 pos = Get32(p + offset); + s += ' '; + s += strName; + if (pos >= size) + return; + p += pos; + size -= pos; + if (size < 8) + return; + if (Get16(p) != 2) // revision + return; + UInt32 num = Get32(p + 4); + AddUInt32ToString(s, num); + + /* + UInt32 aclSize = Get16(p + 2); + if (num >= (1 << 16)) + return; + if (aclSize > size) + return; + size = aclSize; + size -= 8; + p += 8; + for (UInt32 i = 0 ; i < num; i++) + { + if (size <= 8) + return; + // Byte type = p[0]; + // Byte flags = p[1]; + // UInt32 aceSize = Get16(p + 2); + // UInt32 mask = Get32(p + 4); + p += 8; + size -= 8; + + UInt32 sidSize = 0; + s += ' '; + ParseSid(s, p, size, sidSize); + if (sidSize == 0) + return; + p += sidSize; + size -= sidSize; + } + + // the tail can contain zeros. So (size != 0) is not ERROR + // if (size != 0) s += " ERROR"; + */ +} + +#define MY_SE_OWNER_DEFAULTED (0x0001) +#define MY_SE_GROUP_DEFAULTED (0x0002) +#define MY_SE_DACL_PRESENT (0x0004) +#define MY_SE_DACL_DEFAULTED (0x0008) +#define MY_SE_SACL_PRESENT (0x0010) +#define MY_SE_SACL_DEFAULTED (0x0020) +#define MY_SE_DACL_AUTO_INHERIT_REQ (0x0100) +#define MY_SE_SACL_AUTO_INHERIT_REQ (0x0200) +#define MY_SE_DACL_AUTO_INHERITED (0x0400) +#define MY_SE_SACL_AUTO_INHERITED (0x0800) +#define MY_SE_DACL_PROTECTED (0x1000) +#define MY_SE_SACL_PROTECTED (0x2000) +#define MY_SE_RM_CONTROL_VALID (0x4000) +#define MY_SE_SELF_RELATIVE (0x8000) + +void ConvertNtSecureToString(const Byte *data, UInt32 size, AString &s) +{ + s.Empty(); + if (size < 20 || size > (1 << 18)) + { + s += "ERROR"; + return; + } + if (Get16(data) != 1) // revision + { + s += "UNSUPPORTED"; + return; + } + ParseOwner(s, data, size, Get32(data + 4)); + s += ' '; + ParseOwner(s, data, size, Get32(data + 8)); + ParseAcl(s, data, size, "s:", MY_SE_SACL_PRESENT, 12); + ParseAcl(s, data, size, "d:", MY_SE_DACL_PRESENT, 16); + s += ' '; + AddUInt32ToString(s, size); + // s += '\n'; + // s += Data_To_Hex(data, size); +} + +#ifdef _WIN32 + +static bool CheckSid(const Byte *data, UInt32 size, UInt32 pos) throw() +{ + if (pos >= size) + return false; + size -= pos; + if (size < 8) + return false; + UInt32 rev = data[pos]; + if (rev != 1) + return false; + UInt32 num = data[pos + 1]; + return (8 + num * 4 <= size); +} + +static bool CheckAcl(const Byte *p, UInt32 size, UInt32 flags, UInt32 offset) throw() +{ + UInt32 control = Get16(p + 2); + if ((flags & control) == 0) + return true; + UInt32 pos = Get32(p + offset); + if (pos >= size) + return false; + p += pos; + size -= pos; + if (size < 8) + return false; + UInt32 aclSize = Get16(p + 2); + return (aclSize <= size); +} + +bool CheckNtSecure(const Byte *data, UInt32 size) throw() +{ + if (size < 20) + return false; + if (Get16(data) != 1) // revision + return true; // windows function can handle such error, so we allow it + if (size > (1 << 18)) + return false; + if (!CheckSid(data, size, Get32(data + 4))) return false; + if (!CheckSid(data, size, Get32(data + 8))) return false; + if (!CheckAcl(data, size, MY_SE_SACL_PRESENT, 12)) return false; + if (!CheckAcl(data, size, MY_SE_DACL_PRESENT, 16)) return false; + return true; +} + +#endif + +bool ConvertNtReparseToString(const Byte *data, UInt32 size, UString &s) +{ + s.Empty(); + NFile::CReparseAttr attr; + if (attr.Parse(data, size)) + { + if (!attr.IsSymLink()) + s.AddAscii("Junction: "); + s += attr.GetPath(); + if (!attr.IsOkNamePair()) + { + s.AddAscii(" : "); + s += attr.PrintName; + } + return true; + } + + if (size < 8) + return false; + UInt32 tag = Get32(data); + UInt32 len = Get16(data + 4); + if (len + 8 > size) + return false; + if (Get16(data + 6) != 0) // padding + return false; + + char hex[16]; + ConvertUInt32ToHex8Digits(tag, hex); + s.AddAscii(hex); + s.Add_Space(); + + data += 8; + + for (UInt32 i = 0; i < len; i++) + { + unsigned b = ((const Byte *)data)[i]; + s += (wchar_t)GetHex((b >> 4) & 0xF); + s += (wchar_t)GetHex(b & 0xF); + } + return true; +} + +#endif diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/PropIDUtils.h b/3rdparty/lzma/CPP/7zip/UI/Common/PropIDUtils.h index ca14d091da3..1dea321e9ac 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/PropIDUtils.h +++ b/3rdparty/lzma/CPP/7zip/UI/Common/PropIDUtils.h @@ -3,10 +3,16 @@ #ifndef __PROPID_UTILS_H #define __PROPID_UTILS_H -#include "Common/MyString.h" -#include "Common/Types.h" +#include "../../../Common/MyString.h" -void ConvertUInt32ToHex(UInt32 value, wchar_t *s); -UString ConvertPropertyToString(const PROPVARIANT &propVariant, PROPID propID, bool full = true); +// provide at least 64 bytes for buffer including zero-end +void ConvertPropertyToShortString(char *dest, const PROPVARIANT &propVariant, PROPID propID, bool full = true) throw(); +void ConvertPropertyToString(UString &dest, const PROPVARIANT &propVariant, PROPID propID, bool full = true); + +bool ConvertNtReparseToString(const Byte *data, UInt32 size, UString &s); +void ConvertNtSecureToString(const Byte *data, UInt32 size, AString &s); +bool CheckNtSecure(const Byte *data, UInt32 size) throw();; + +void ConvertWinAttribToString(char *s, UInt32 wa) throw(); #endif diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/SetProperties.cpp b/3rdparty/lzma/CPP/7zip/UI/Common/SetProperties.cpp index ac9b59a381d..c3de5d5a323 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/SetProperties.cpp +++ b/3rdparty/lzma/CPP/7zip/UI/Common/SetProperties.cpp @@ -2,25 +2,26 @@ #include "StdAfx.h" -#include "SetProperties.h" +#include "../../../Common/MyCom.h" +#include "../../../Common/MyString.h" +#include "../../../Common/StringToInt.h" -#include "Windows/PropVariant.h" -#include "Common/MyString.h" -#include "Common/StringToInt.h" -#include "Common/MyCom.h" +#include "../../../Windows/PropVariant.h" #include "../../Archive/IArchive.h" +#include "SetProperties.h" + using namespace NWindows; using namespace NCOM; static void ParseNumberString(const UString &s, NCOM::CPropVariant &prop) { - const wchar_t *endPtr; - UInt64 result = ConvertStringToUInt64(s, &endPtr); - if (endPtr - (const wchar_t *)s != s.Length()) + const wchar_t *end; + UInt64 result = ConvertStringToUInt64(s, &end); + if (*end != 0 || s.IsEmpty()) prop = s; - else if (result <= 0xFFFFFFFF) + else if (result <= (UInt32)0xFFFFFFFF) prop = (UInt32)result; else prop = result; @@ -39,8 +40,8 @@ HRESULT SetProperties(IUnknown *unknown, const CObjectVector<CProperty> &propert CPropVariant *values = new CPropVariant[properties.Size()]; try { - int i; - for(i = 0; i < properties.Size(); i++) + unsigned i; + for (i = 0; i < properties.Size(); i++) { const CProperty &property = properties[i]; NCOM::CPropVariant propVariant; @@ -64,7 +65,7 @@ HRESULT SetProperties(IUnknown *unknown, const CObjectVector<CProperty> &propert values[i] = propVariant; } CRecordVector<const wchar_t *> names; - for(i = 0; i < realNames.Size(); i++) + for (i = 0; i < realNames.Size(); i++) names.Add((const wchar_t *)realNames[i]); RINOK(setProperties->SetProperties(&names.Front(), values, names.Size())); diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/SortUtils.cpp b/3rdparty/lzma/CPP/7zip/UI/Common/SortUtils.cpp index 061e7773079..5f29249bdeb 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/SortUtils.cpp +++ b/3rdparty/lzma/CPP/7zip/UI/Common/SortUtils.cpp @@ -2,21 +2,24 @@ #include "StdAfx.h" +#include "../../../Common/Wildcard.h" + #include "SortUtils.h" -#include "Common/Wildcard.h" -static int CompareStrings(const int *p1, const int *p2, void *param) +static int CompareStrings(const unsigned *p1, const unsigned *p2, void *param) { const UStringVector &strings = *(const UStringVector *)param; return CompareFileNames(strings[*p1], strings[*p2]); } -void SortFileNames(const UStringVector &strings, CIntVector &indices) +void SortFileNames(const UStringVector &strings, CUIntVector &indices) { - indices.Clear(); - int numItems = strings.Size(); - indices.Reserve(numItems); - for(int i = 0; i < numItems; i++) - indices.Add(i); + const unsigned numItems = strings.Size(); + indices.ClearAndSetSize(numItems); + if (numItems == 0) + return; + unsigned *vals = &indices[0]; + for (unsigned i = 0; i < numItems; i++) + vals[i] = i; indices.Sort(CompareStrings, (void *)&strings); } diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/SortUtils.h b/3rdparty/lzma/CPP/7zip/UI/Common/SortUtils.h index e15224611b7..8e42e06827a 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/SortUtils.h +++ b/3rdparty/lzma/CPP/7zip/UI/Common/SortUtils.h @@ -1,10 +1,10 @@ // SortUtils.h -#ifndef __SORTUTLS_H -#define __SORTUTLS_H +#ifndef __SORT_UTLS_H +#define __SORT_UTLS_H -#include "Common/MyString.h" +#include "../../../Common/MyString.h" -void SortFileNames(const UStringVector &strings, CIntVector &indices); +void SortFileNames(const UStringVector &strings, CUIntVector &indices); #endif diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/StdAfx.h b/3rdparty/lzma/CPP/7zip/UI/Common/StdAfx.h index 9a8e7d21a28..2854ff3e935 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/StdAfx.h +++ b/3rdparty/lzma/CPP/7zip/UI/Common/StdAfx.h @@ -1,9 +1,8 @@ -// stdafx.h +// StdAfx.h #ifndef __STDAFX_H #define __STDAFX_H -#include "../../../Common/MyWindows.h" -#include "../../../Common/NewHandler.h" +#include "../../../Common/Common.h" #endif diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/TempFiles.cpp b/3rdparty/lzma/CPP/7zip/UI/Common/TempFiles.cpp index c799c7bfa4f..2f868381f9b 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/TempFiles.cpp +++ b/3rdparty/lzma/CPP/7zip/UI/Common/TempFiles.cpp @@ -2,9 +2,9 @@ #include "StdAfx.h" -#include "TempFiles.h" +#include "../../../Windows/FileDir.h" -#include "Windows/FileDir.h" +#include "TempFiles.h" using namespace NWindows; using namespace NFile; @@ -13,9 +13,7 @@ void CTempFiles::Clear() { while (!Paths.IsEmpty()) { - NDirectory::DeleteFileAlways(Paths.Back()); + NDir::DeleteFileAlways(Paths.Back()); Paths.DeleteBack(); } } - - diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/TempFiles.h b/3rdparty/lzma/CPP/7zip/UI/Common/TempFiles.h index 5dcda5b63ee..4099e655898 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/TempFiles.h +++ b/3rdparty/lzma/CPP/7zip/UI/Common/TempFiles.h @@ -3,7 +3,7 @@ #ifndef __TEMP_FILES_H #define __TEMP_FILES_H -#include "Common/MyString.h" +#include "../../../Common/MyString.h" class CTempFiles { diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/Update.cpp b/3rdparty/lzma/CPP/7zip/UI/Common/Update.cpp index e740e8a70f7..2dfb210458d 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/Update.cpp +++ b/3rdparty/lzma/CPP/7zip/UI/Common/Update.cpp @@ -4,18 +4,19 @@ #include "Update.h" -#include "Common/IntToString.h" -#include "Common/StringConvert.h" +#include "../../../Common/IntToString.h" +#include "../../../Common/StringConvert.h" -#include "Windows/DLL.h" -#include "Windows/FileDir.h" -#include "Windows/FileFind.h" -#include "Windows/FileName.h" -#include "Windows/PropVariant.h" -#include "Windows/PropVariantConversions.h" -#include "Windows/Time.h" +#include "../../../Windows/DLL.h" +#include "../../../Windows/FileDir.h" +#include "../../../Windows/FileFind.h" +#include "../../../Windows/FileName.h" +#include "../../../Windows/PropVariant.h" +#include "../../../Windows/PropVariantConv.h" +#include "../../../Windows/TimeUtils.h" #include "../../Common/FileStreams.h" +#include "../../Common/LimitedStreams.h" #include "../../Compress/CopyCoder.h" @@ -35,22 +36,59 @@ static const char *kUpdateIsNotSupoorted = using namespace NWindows; using namespace NCOM; using namespace NFile; +using namespace NDir; using namespace NName; static CFSTR kTempFolderPrefix = FTEXT("7zE"); + +void CUpdateErrorInfo::SetFromLastError(const char *message) +{ + SystemError = ::GetLastError(); + Message = message; +} + +HRESULT CUpdateErrorInfo::SetFromLastError(const char *message, const FString &fileName) +{ + SetFromLastError(message); + FileNames.Add(fileName); + return Get_HRESULT_Error(); +} + +static bool DeleteEmptyFolderAndEmptySubFolders(const FString &path) +{ + NFind::CFileInfo fileInfo; + FString pathPrefix = path + FCHAR_PATH_SEPARATOR; + { + NFind::CEnumerator enumerator(pathPrefix + FCHAR_ANY_MASK); + while (enumerator.Next(fileInfo)) + { + if (fileInfo.IsDir()) + if (!DeleteEmptyFolderAndEmptySubFolders(pathPrefix + fileInfo.Name)) + return false; + } + } + /* + // we don't need clear read-only for folders + if (!MySetFileAttributes(path, 0)) + return false; + */ + return RemoveDir(path); +} + + using namespace NUpdateArchive; class COutMultiVolStream: public IOutStream, public CMyUnknownImp { - int _streamIndex; // required stream + unsigned _streamIndex; // required stream UInt64 _offsetPos; // offset from start of _streamIndex index UInt64 _absPos; UInt64 _length; - struct CSubStreamInfo + struct CAltStreamInfo { COutFileStream *StreamSpec; CMyComPtr<IOutStream> Stream; @@ -58,7 +96,7 @@ class COutMultiVolStream: UInt64 Pos; UInt64 RealSize; }; - CObjectVector<CSubStreamInfo> Streams; + CObjectVector<CAltStreamInfo> Streams; public: // CMyComPtr<IArchiveUpdateCallback2> VolumeCallback; CRecordVector<UInt64> Sizes; @@ -73,8 +111,11 @@ public: _length = 0; } + bool SetMTime(const FILETIME *mTime); HRESULT Close(); + UInt64 GetSize() const { return _length; } + MY_UNKNOWN_IMP1(IOutStream) STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); @@ -87,12 +128,12 @@ public: HRESULT COutMultiVolStream::Close() { HRESULT res = S_OK; - for (int i = 0; i < Streams.Size(); i++) + FOR_VECTOR (i, Streams) { - CSubStreamInfo &s = Streams[i]; - if (s.StreamSpec) + COutFileStream *s = Streams[i].StreamSpec; + if (s) { - HRESULT res2 = s.StreamSpec->Close(); + HRESULT res2 = s->Close(); if (res2 != S_OK) res = res2; } @@ -100,6 +141,19 @@ HRESULT COutMultiVolStream::Close() return res; } +bool COutMultiVolStream::SetMTime(const FILETIME *mTime) +{ + bool res = true; + FOR_VECTOR (i, Streams) + { + COutFileStream *s = Streams[i].StreamSpec; + if (s) + if (!s->SetMTime(mTime)) + res = false; + } + return res; +} + STDMETHODIMP COutMultiVolStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { if (processedSize != NULL) @@ -108,32 +162,32 @@ STDMETHODIMP COutMultiVolStream::Write(const void *data, UInt32 size, UInt32 *pr { if (_streamIndex >= Streams.Size()) { - CSubStreamInfo subStream; + CAltStreamInfo altStream; FChar temp[16]; ConvertUInt32ToString(_streamIndex + 1, temp); - FString res = temp; - while (res.Length() < 3) - res = FString(FTEXT('0')) + res; - FString name = Prefix + res; - subStream.StreamSpec = new COutFileStream; - subStream.Stream = subStream.StreamSpec; - if (!subStream.StreamSpec->Create(name, false)) + FString name = temp; + while (name.Len() < 3) + name.InsertAtFront(FTEXT('0')); + name.Insert(0, Prefix); + altStream.StreamSpec = new COutFileStream; + altStream.Stream = altStream.StreamSpec; + if (!altStream.StreamSpec->Create(name, false)) return ::GetLastError(); { // NSynchronization::CCriticalSectionLock lock(g_TempPathsCS); TempFiles->Paths.Add(name); } - subStream.Pos = 0; - subStream.RealSize = 0; - subStream.Name = name; - Streams.Add(subStream); + altStream.Pos = 0; + altStream.RealSize = 0; + altStream.Name = name; + Streams.Add(altStream); continue; } - CSubStreamInfo &subStream = Streams[_streamIndex]; + CAltStreamInfo &altStream = Streams[_streamIndex]; - int index = _streamIndex; + unsigned index = _streamIndex; if (index >= Sizes.Size()) index = Sizes.Size() - 1; UInt64 volSize = Sizes[index]; @@ -144,29 +198,29 @@ STDMETHODIMP COutMultiVolStream::Write(const void *data, UInt32 size, UInt32 *pr _streamIndex++; continue; } - if (_offsetPos != subStream.Pos) + if (_offsetPos != altStream.Pos) { // CMyComPtr<IOutStream> outStream; - // RINOK(subStream.Stream.QueryInterface(IID_IOutStream, &outStream)); - RINOK(subStream.Stream->Seek(_offsetPos, STREAM_SEEK_SET, NULL)); - subStream.Pos = _offsetPos; + // RINOK(altStream.Stream.QueryInterface(IID_IOutStream, &outStream)); + RINOK(altStream.Stream->Seek(_offsetPos, STREAM_SEEK_SET, NULL)); + altStream.Pos = _offsetPos; } - UInt32 curSize = (UInt32)MyMin((UInt64)size, volSize - subStream.Pos); + UInt32 curSize = (UInt32)MyMin((UInt64)size, volSize - altStream.Pos); UInt32 realProcessed; - RINOK(subStream.Stream->Write(data, curSize, &realProcessed)); + RINOK(altStream.Stream->Write(data, curSize, &realProcessed)); data = (void *)((Byte *)data + realProcessed); size -= realProcessed; - subStream.Pos += realProcessed; + altStream.Pos += realProcessed; _offsetPos += realProcessed; _absPos += realProcessed; if (_absPos > _length) _length = _absPos; - if (_offsetPos > subStream.RealSize) - subStream.RealSize = _offsetPos; + if (_offsetPos > altStream.RealSize) + altStream.RealSize = _offsetPos; if (processedSize != NULL) *processedSize += realProcessed; - if (subStream.Pos == volSize) + if (altStream.Pos == volSize) { _streamIndex++; _offsetPos = 0; @@ -182,17 +236,11 @@ STDMETHODIMP COutMultiVolStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *n { if (seekOrigin >= 3) return STG_E_INVALIDFUNCTION; - switch(seekOrigin) + switch (seekOrigin) { - case STREAM_SEEK_SET: - _absPos = offset; - break; - case STREAM_SEEK_CUR: - _absPos += offset; - break; - case STREAM_SEEK_END: - _absPos = _length + offset; - break; + case STREAM_SEEK_SET: _absPos = offset; break; + case STREAM_SEEK_CUR: _absPos += offset; break; + case STREAM_SEEK_END: _absPos = _length + offset; break; } _offsetPos = _absPos; if (newPosition != NULL) @@ -205,24 +253,24 @@ STDMETHODIMP COutMultiVolStream::SetSize(UInt64 newSize) { if (newSize < 0) return E_INVALIDARG; - int i = 0; + unsigned i = 0; while (i < Streams.Size()) { - CSubStreamInfo &subStream = Streams[i++]; - if ((UInt64)newSize < subStream.RealSize) + CAltStreamInfo &altStream = Streams[i++]; + if ((UInt64)newSize < altStream.RealSize) { - RINOK(subStream.Stream->SetSize(newSize)); - subStream.RealSize = newSize; + RINOK(altStream.Stream->SetSize(newSize)); + altStream.RealSize = newSize; break; } - newSize -= subStream.RealSize; + newSize -= altStream.RealSize; } while (i < Streams.Size()) { { - CSubStreamInfo &subStream = Streams.Back(); - subStream.Stream.Release(); - NDirectory::DeleteFileAlways(subStream.Name); + CAltStreamInfo &altStream = Streams.Back(); + altStream.Stream.Release(); + DeleteFileAlways(altStream.Name); } Streams.DeleteBack(); } @@ -232,53 +280,140 @@ STDMETHODIMP COutMultiVolStream::SetSize(UInt64 newSize) return S_OK; } -static const wchar_t *kDefaultArchiveType = L"7z"; -static const wchar_t *kSFXExtension = +void CArchivePath::ParseFromPath(const UString &path, EArcNameMode mode) +{ + OriginalPath = path; + + SplitPathToParts_2(path, Prefix, Name); + + if (mode == k_ArcNameMode_Add) + return; + if (mode == k_ArcNameMode_Exact) + { + BaseExtension.Empty(); + return; + } + + int dotPos = Name.ReverseFind_Dot(); + if (dotPos < 0) + return; + if ((unsigned)dotPos == Name.Len() - 1) + { + Name.DeleteBack(); + BaseExtension.Empty(); + return; + } + const UString ext = Name.Ptr(dotPos + 1); + if (BaseExtension.IsEqualTo_NoCase(ext)) + { + BaseExtension = ext; + Name.DeleteFrom(dotPos); + } + else + BaseExtension.Empty(); +} + +UString CArchivePath::GetFinalPath() const +{ + UString path = GetPathWithoutExt(); + if (!BaseExtension.IsEmpty()) + { + path += L'.'; + path += BaseExtension; + } + return path; +} + +UString CArchivePath::GetFinalVolPath() const +{ + UString path = GetPathWithoutExt(); + if (!BaseExtension.IsEmpty()) + { + path += L'.'; + path += VolExtension; + } + return path; +} + +FString CArchivePath::GetTempPath() const +{ + FString path = TempPrefix; + path += us2fs(Name); + if (!BaseExtension.IsEmpty()) + { + path += FTEXT('.'); + path += us2fs(BaseExtension); + } + path.AddAscii(".tmp"); + path += TempPostfix; + return path; +} + +static const wchar_t *kDefaultArcType = L"7z"; +static const wchar_t *kDefaultArcExt = L"7z"; +static const char *kSFXExtension = #ifdef _WIN32 - L"exe"; + "exe"; #else - L""; + ""; #endif -bool CUpdateOptions::Init(const CCodecs *codecs, const CIntVector &formatIndices, const UString &arcPath) +bool CUpdateOptions::InitFormatIndex(const CCodecs *codecs, + const CObjectVector<COpenType> &types, const UString &arcPath) { - if (formatIndices.Size() > 1) + if (types.Size() > 1) return false; - int arcTypeIndex = -1; - if (formatIndices.Size() != 0) - arcTypeIndex = formatIndices[0]; - if (arcTypeIndex >= 0) - MethodMode.FormatIndex = arcTypeIndex; + // int arcTypeIndex = -1; + if (types.Size() != 0) + { + MethodMode.Type = types[0]; + MethodMode.Type_Defined = true; + } + if (MethodMode.Type.FormatIndex < 0) + { + // MethodMode.Type = -1; + MethodMode.Type = COpenType(); + if (ArcNameMode != k_ArcNameMode_Add) + { + MethodMode.Type.FormatIndex = codecs->FindFormatForArchiveName(arcPath); + if (MethodMode.Type.FormatIndex >= 0) + MethodMode.Type_Defined = true; + } + } + return true; +} + +bool CUpdateOptions::SetArcPath(const CCodecs *codecs, const UString &arcPath) +{ + UString typeExt; + int formatIndex = MethodMode.Type.FormatIndex; + if (formatIndex < 0) + { + typeExt = kDefaultArcExt; + } else { - MethodMode.FormatIndex = codecs->FindFormatForArchiveName(arcPath); - // It works incorrectly for update command if archive has some non-default extension! - if (MethodMode.FormatIndex < 0) - MethodMode.FormatIndex = codecs->FindFormatForArchiveType(kDefaultArchiveType); + const CArcInfoEx &arcInfo = codecs->Formats[formatIndex]; + if (!arcInfo.UpdateEnabled) + return false; + typeExt = arcInfo.GetMainExt(); } - if (MethodMode.FormatIndex < 0) - return false; - const CArcInfoEx &arcInfo = codecs->Formats[MethodMode.FormatIndex]; - if (!arcInfo.UpdateEnabled) - return false; - UString typeExt = arcInfo.GetMainExt(); UString ext = typeExt; if (SfxMode) - ext = kSFXExtension; + ext.SetFromAscii(kSFXExtension); ArchivePath.BaseExtension = ext; ArchivePath.VolExtension = typeExt; - ArchivePath.ParseFromPath(arcPath); - for (int i = 0; i < Commands.Size(); i++) + ArchivePath.ParseFromPath(arcPath, ArcNameMode); + FOR_VECTOR (i, Commands) { CUpdateArchiveCommand &uc = Commands[i]; uc.ArchivePath.BaseExtension = ext; uc.ArchivePath.VolExtension = typeExt; - uc.ArchivePath.ParseFromPath(uc.UserArchivePath); + uc.ArchivePath.ParseFromPath(uc.UserArchivePath, ArcNameMode); } return true; } -/* struct CUpdateProduceCallbackImp: public IUpdateProduceCallback { const CObjectVector<CArcItem> *_arcItems; @@ -286,45 +421,104 @@ struct CUpdateProduceCallbackImp: public IUpdateProduceCallback CUpdateProduceCallbackImp(const CObjectVector<CArcItem> *a, IUpdateCallbackUI *callback): _arcItems(a), _callback(callback) {} - virtual HRESULT ShowDeleteFile(int arcIndex); + virtual HRESULT ShowDeleteFile(unsigned arcIndex); }; -HRESULT CUpdateProduceCallbackImp::ShowDeleteFile(int arcIndex) +HRESULT CUpdateProduceCallbackImp::ShowDeleteFile(unsigned arcIndex) +{ + const CArcItem &ai = (*_arcItems)[arcIndex]; + return _callback->ShowDeleteFile(ai.Name, ai.IsDir); +} + +bool CRenamePair::Prepare() +{ + if (RecursedType != NRecursedType::kNonRecursed) + return false; + if (!WildcardParsing) + return true; + return !DoesNameContainWildcard(OldName); +} + +extern bool g_CaseSensitive; + +static unsigned CompareTwoNames(const wchar_t *s1, const wchar_t *s2) +{ + for (unsigned i = 0;; i++) + { + wchar_t c1 = s1[i]; + wchar_t c2 = s2[i]; + if (c1 == 0 || c2 == 0) + return i; + if (c1 == c2) + continue; + if (!g_CaseSensitive && (MyCharUpper(c1) == MyCharUpper(c2))) + continue; + if (IsPathSepar(c1) && IsPathSepar(c2)) + continue; + return i; + } +} + +bool CRenamePair::GetNewPath(bool isFolder, const UString &src, UString &dest) const { - return _callback->ShowDeleteFile((*_arcItems)[arcIndex].Name); + unsigned num = CompareTwoNames(OldName, src); + if (OldName[num] == 0) + { + if (src[num] != 0 && !IsPathSepar(src[num]) && num != 0 && !IsPathSepar(src[num - 1])) + return false; + } + else + { + // OldName[num] != 0 + // OldName = "1\1a.txt" + // src = "1" + + if (!isFolder + || src[num] != 0 + || !IsPathSepar(OldName[num]) + || OldName[num + 1] != 0) + return false; + } + dest = NewName + src.Ptr(num); + return true; } -*/ + +#ifdef SUPPORT_ALT_STREAMS +int FindAltStreamColon_in_Path(const wchar_t *path); +#endif static HRESULT Compress( + const CUpdateOptions &options, + bool isUpdatingItself, CCodecs *codecs, const CActionSet &actionSet, - IInArchive *archive, - const CCompressionMethodMode &compressionMethod, + const CArc *arc, CArchivePath &archivePath, const CObjectVector<CArcItem> &arcItems, - bool shareForWrite, - bool stdInMode, - /* const UString & stdInFileName, */ - bool stdOutMode, + Byte *processedItemsStatuses, const CDirItems &dirItems, - bool sfxMode, - const FString &sfxModule, - const CRecordVector<UInt64> &volumesSizes, + const CDirItem *parentDirItem, CTempFiles &tempFiles, CUpdateErrorInfo &errorInfo, - IUpdateCallbackUI *callback) + IUpdateCallbackUI *callback, + CFinishArchiveStat &st) { CMyComPtr<IOutArchive> outArchive; - if (archive != NULL) + int formatIndex = options.MethodMode.Type.FormatIndex; + + if (arc) { - CMyComPtr<IInArchive> archive2 = archive; + formatIndex = arc->FormatIndex; + if (formatIndex < 0) + return E_NOTIMPL; + CMyComPtr<IInArchive> archive2 = arc->Archive; HRESULT result = archive2.QueryInterface(IID_IOutArchive, &outArchive); if (result != S_OK) throw kUpdateIsNotSupoorted; } else { - RINOK(codecs->CreateOutArchive(compressionMethod.FormatIndex, outArchive)); + RINOK(codecs->CreateOutArchive(formatIndex, outArchive)); #ifdef EXTERNAL_CODECS { @@ -337,75 +531,170 @@ static HRESULT Compress( } #endif } + if (outArchive == 0) throw kUpdateIsNotSupoorted; NFileTimeType::EEnum fileTimeType; - UInt32 value; - RINOK(outArchive->GetFileTimeType(&value)); + { + UInt32 value; + RINOK(outArchive->GetFileTimeType(&value)); + + switch (value) + { + case NFileTimeType::kWindows: + case NFileTimeType::kUnix: + case NFileTimeType::kDOS: + fileTimeType = (NFileTimeType::EEnum)value; + break; + default: + return E_FAIL; + } + } - switch(value) { - case NFileTimeType::kWindows: - case NFileTimeType::kUnix: - case NFileTimeType::kDOS: - fileTimeType = (NFileTimeType::EEnum)value; - break; - default: - return E_FAIL; + const CArcInfoEx &arcInfo = codecs->Formats[formatIndex]; + if (options.AltStreams.Val && !arcInfo.Flags_AltStreams()) + return E_NOTIMPL; + if (options.NtSecurity.Val && !arcInfo.Flags_NtSecure()) + return E_NOTIMPL; } CRecordVector<CUpdatePair2> updatePairs2; + UStringVector newNames; + + if (options.RenamePairs.Size() != 0) + { + FOR_VECTOR (i, arcItems) + { + const CArcItem &ai = arcItems[i]; + bool needRename = false; + UString dest; + + if (ai.Censored) + { + FOR_VECTOR (j, options.RenamePairs) + { + const CRenamePair &rp = options.RenamePairs[j]; + if (rp.GetNewPath(ai.IsDir, ai.Name, dest)) + { + needRename = true; + break; + } + + #ifdef SUPPORT_ALT_STREAMS + if (ai.IsAltStream) + { + int colonPos = FindAltStreamColon_in_Path(ai.Name); + if (colonPos >= 0) + { + UString mainName = ai.Name.Left(colonPos); + /* + actually we must improve that code to support cases + with folder renaming like: rn arc dir1\ dir2\ + */ + if (rp.GetNewPath(false, mainName, dest)) + { + needRename = true; + dest += L':'; + dest += ai.Name.Ptr(colonPos + 1); + break; + } + } + } + #endif + } + } + + CUpdatePair2 up2; + up2.SetAs_NoChangeArcItem(ai.IndexInServer); + if (needRename) + { + up2.NewProps = true; + RINOK(arc->IsItemAnti(i, up2.IsAnti)); + up2.NewNameIndex = newNames.Add(dest); + } + updatePairs2.Add(up2); + } + } + else { CRecordVector<CUpdatePair> updatePairs; GetUpdatePairInfoList(dirItems, arcItems, fileTimeType, updatePairs); // must be done only once!!! - // CUpdateProduceCallbackImp upCallback(&arcItems, callback); - UpdateProduce(updatePairs, actionSet, updatePairs2, NULL /* &upCallback */); + CUpdateProduceCallbackImp upCallback(&arcItems, callback); + + UpdateProduce(updatePairs, actionSet, updatePairs2, isUpdatingItself ? &upCallback : NULL); } - UInt32 numFiles = 0; - for (int i = 0; i < updatePairs2.Size(); i++) - if (updatePairs2[i].NewData) - numFiles++; - - RINOK(callback->SetNumFiles(numFiles)); - + { + UInt32 numItems = 0; + FOR_VECTOR (i, updatePairs2) + if (updatePairs2[i].NewData) + numItems++; + RINOK(callback->SetNumItems(numItems)); + } CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback; CMyComPtr<IArchiveUpdateCallback> updateCallback(updateCallbackSpec); - updateCallbackSpec->ShareForWrite = shareForWrite; - updateCallbackSpec->StdInMode = stdInMode; + updateCallbackSpec->ShareForWrite = options.OpenShareForWrite; + updateCallbackSpec->StdInMode = options.StdInMode; updateCallbackSpec->Callback = callback; + + if (arc) + { + // we set Archive to allow to transfer GetProperty requests back to DLL. + updateCallbackSpec->Archive = arc->Archive; + } + updateCallbackSpec->DirItems = &dirItems; + updateCallbackSpec->ParentDirItem = parentDirItem; + + updateCallbackSpec->StoreNtSecurity = options.NtSecurity.Val; + updateCallbackSpec->StoreHardLinks = options.HardLinks.Val; + updateCallbackSpec->StoreSymLinks = options.SymLinks.Val; + + updateCallbackSpec->Arc = arc; updateCallbackSpec->ArcItems = &arcItems; updateCallbackSpec->UpdatePairs = &updatePairs2; + updateCallbackSpec->ProcessedItemsStatuses = processedItemsStatuses; + + if (options.RenamePairs.Size() != 0) + updateCallbackSpec->NewNames = &newNames; + + CMyComPtr<IOutStream> outSeekStream; CMyComPtr<ISequentialOutStream> outStream; - if (!stdOutMode) + if (!options.StdOutMode) { FString dirPrefix; - if (!NFile::NDirectory::GetOnlyDirPrefix(us2fs(archivePath.GetFinalPath()), dirPrefix)) + if (!GetOnlyDirPrefix(us2fs(archivePath.GetFinalPath()), dirPrefix)) throw 1417161; - NFile::NDirectory::CreateComplexDirectory(dirPrefix); + CreateComplexDir(dirPrefix); } COutFileStream *outStreamSpec = NULL; + CStdOutFileStream *stdOutFileStreamSpec = NULL; COutMultiVolStream *volStreamSpec = NULL; - if (volumesSizes.Size() == 0) + if (options.VolumesSizes.Size() == 0) { - if (stdOutMode) - outStream = new CStdOutFileStream; + if (options.StdOutMode) + { + stdOutFileStreamSpec = new CStdOutFileStream; + outStream = stdOutFileStreamSpec; + } else { outStreamSpec = new COutFileStream; - outStream = outStreamSpec; + outSeekStream = outStreamSpec; + outStream = outSeekStream; bool isOK = false; FString realPath; - for (int i = 0; i < (1 << 16); i++) + + for (unsigned i = 0; i < (1 << 16); i++) { if (archivePath.Temp) { @@ -430,23 +719,24 @@ static HRESULT Compress( if (!archivePath.Temp) break; } + if (!isOK) - { - errorInfo.SystemError = ::GetLastError(); - errorInfo.FileName = realPath; - errorInfo.Message = L"7-Zip cannot open file"; - return E_FAIL; - } + return errorInfo.SetFromLastError("cannot open file", realPath); } } else { - if (stdOutMode) + if (options.StdOutMode) return E_FAIL; + if (arc && arc->GetGlobalOffset() > 0) + return E_NOTIMPL; + volStreamSpec = new COutMultiVolStream; - outStream = volStreamSpec; - volStreamSpec->Sizes = volumesSizes; - volStreamSpec->Prefix = us2fs(archivePath.GetFinalPath() + L"."); + outSeekStream = volStreamSpec; + outStream = outSeekStream; + volStreamSpec->Sizes = options.VolumesSizes; + volStreamSpec->Prefix = us2fs(archivePath.GetFinalVolPath()); + volStreamSpec->Prefix += FTEXT('.'); volStreamSpec->TempFiles = &tempFiles; volStreamSpec->Init(); @@ -458,23 +748,18 @@ static HRESULT Compress( */ } - RINOK(SetProperties(outArchive, compressionMethod.Properties)); + RINOK(SetProperties(outArchive, options.MethodMode.Properties)); - if (sfxMode) + if (options.SfxMode) { CInFileStream *sfxStreamSpec = new CInFileStream; CMyComPtr<IInStream> sfxStream(sfxStreamSpec); - if (!sfxStreamSpec->Open(sfxModule)) - { - errorInfo.SystemError = ::GetLastError(); - errorInfo.Message = L"7-Zip cannot open SFX module"; - errorInfo.FileName = sfxModule; - return E_FAIL; - } + if (!sfxStreamSpec->Open(options.SfxModule)) + return errorInfo.SetFromLastError("cannot open SFX module", options.SfxModule); CMyComPtr<ISequentialOutStream> sfxOutStream; COutFileStream *outStreamSpec = NULL; - if (volumesSizes.Size() == 0) + if (options.VolumesSizes.Size() == 0) sfxOutStream = outStream; else { @@ -482,23 +767,97 @@ static HRESULT Compress( sfxOutStream = outStreamSpec; FString realPath = us2fs(archivePath.GetFinalPath()); if (!outStreamSpec->Create(realPath, false)) - { - errorInfo.SystemError = ::GetLastError(); - errorInfo.FileName = realPath; - errorInfo.Message = L"7-Zip cannot open file"; - return E_FAIL; - } + return errorInfo.SetFromLastError("cannot open file", realPath); + } + + { + UInt64 sfxSize; + RINOK(sfxStreamSpec->GetSize(&sfxSize)); + RINOK(callback->WriteSfx(fs2us(options.SfxModule), sfxSize)); } + RINOK(NCompress::CopyStream(sfxStream, sfxOutStream, NULL)); + if (outStreamSpec) { RINOK(outStreamSpec->Close()); } } - HRESULT result = outArchive->UpdateItems(outStream, updatePairs2.Size(), updateCallback); - callback->Finilize(); + CMyComPtr<ISequentialOutStream> tailStream; + + if (options.SfxMode || !arc || arc->ArcStreamOffset == 0) + tailStream = outStream; + else + { + // Int64 globalOffset = arc->GetGlobalOffset(); + RINOK(arc->InStream->Seek(0, STREAM_SEEK_SET, NULL)); + RINOK(NCompress::CopyStream_ExactSize(arc->InStream, outStream, arc->ArcStreamOffset, NULL)); + if (options.StdOutMode) + tailStream = outStream; + else + { + CTailOutStream *tailStreamSpec = new CTailOutStream; + tailStream = tailStreamSpec; + tailStreamSpec->Stream = outSeekStream; + tailStreamSpec->Offset = arc->ArcStreamOffset; + tailStreamSpec->Init(); + } + } + + + HRESULT result = outArchive->UpdateItems(tailStream, updatePairs2.Size(), updateCallback); + // callback->Finalize(); RINOK(result); + + if (!updateCallbackSpec->AreAllFilesClosed()) + { + errorInfo.Message = "There are unclosed input file:"; + errorInfo.FileNames = updateCallbackSpec->_openFiles_Paths; + return E_FAIL; + } + + if (options.SetArcMTime) + { + FILETIME ft; + ft.dwLowDateTime = 0; + ft.dwHighDateTime = 0; + FOR_VECTOR (i, updatePairs2) + { + CUpdatePair2 &pair2 = updatePairs2[i]; + const FILETIME *ft2 = NULL; + if (pair2.NewProps && pair2.DirIndex >= 0) + ft2 = &dirItems.Items[pair2.DirIndex].MTime; + else if (pair2.UseArcProps && pair2.ArcIndex >= 0) + ft2 = &arcItems[pair2.ArcIndex].MTime; + if (ft2) + { + if (::CompareFileTime(&ft, ft2) < 0) + ft = *ft2; + } + } + if (ft.dwLowDateTime != 0 || ft.dwHighDateTime != 0) + { + if (outStreamSpec) + outStreamSpec->SetMTime(&ft); + else if (volStreamSpec) + volStreamSpec->SetMTime(&ft);; + } + } + + if (callback) + { + UInt64 size = 0; + if (outStreamSpec) + outStreamSpec->GetSize(&size); + else if (stdOutFileStreamSpec) + size = stdOutFileStreamSpec->GetSize(); + else + size = volStreamSpec->GetSize(); + + st.OutArcFileSize = size; + } + if (outStreamSpec) result = outStreamSpec->Close(); else if (volStreamSpec) @@ -506,7 +865,27 @@ static HRESULT Compress( return result; } -HRESULT EnumerateInArchiveItems(const NWildcard::CCensor &censor, +bool CensorNode_CheckPath2(const NWildcard::CCensorNode &node, const CReadArcItem &item, bool &include); + +static bool Censor_CheckPath(const NWildcard::CCensor &censor, const CReadArcItem &item) +{ + bool finded = false; + FOR_VECTOR (i, censor.Pairs) + { + bool include; + if (CensorNode_CheckPath2(censor.Pairs[i].Head, item, include)) + { + if (!include) + return false; + finded = true; + } + } + return finded; +} + +static HRESULT EnumerateInArchiveItems( + // bool storeStreamsMode, + const NWildcard::CCensor &censor, const CArc &arc, CObjectVector<CArcItem> &arcItems) { @@ -514,14 +893,30 @@ HRESULT EnumerateInArchiveItems(const NWildcard::CCensor &censor, UInt32 numItems; IInArchive *archive = arc.Archive; RINOK(archive->GetNumberOfItems(&numItems)); - arcItems.Reserve(numItems); + arcItems.ClearAndReserve(numItems); + + CReadArcItem item; + for (UInt32 i = 0; i < numItems; i++) { CArcItem ai; - RINOK(arc.GetItemPath(i, ai.Name)); - RINOK(IsArchiveItemFolder(archive, i, ai.IsDir)); - ai.Censored = censor.CheckPath(ai.Name, !ai.IsDir); + RINOK(arc.GetItem(i, item)); + ai.Name = item.Path; + ai.IsDir = item.IsDir; + ai.IsAltStream = + #ifdef SUPPORT_ALT_STREAMS + item.IsAltStream; + #else + false; + #endif + + /* + if (!storeStreamsMode && ai.IsAltStream) + continue; + */ + ai.Censored = Censor_CheckPath(censor, item); + RINOK(arc.GetItemMTime(i, ai.MTime, ai.MTimeDefined)); RINOK(arc.GetItemSize(i, ai.Size, ai.SizeDefined)); @@ -531,7 +926,7 @@ HRESULT EnumerateInArchiveItems(const NWildcard::CCensor &censor, if (prop.vt == VT_UI4) { ai.TimeType = (int)(NFileTimeType::EEnum)prop.ulVal; - switch(ai.TimeType) + switch (ai.TimeType) { case NFileTimeType::kWindows: case NFileTimeType::kUnix: @@ -544,110 +939,99 @@ HRESULT EnumerateInArchiveItems(const NWildcard::CCensor &censor, } ai.IndexInServer = i; - arcItems.Add(ai); + arcItems.AddInReserved(ai); } return S_OK; } +#if defined(_WIN32) && !defined(UNDER_CE) -static HRESULT UpdateWithItemLists( - CCodecs *codecs, - CUpdateOptions &options, - IInArchive *archive, - const CObjectVector<CArcItem> &arcItems, - CDirItems &dirItems, - CTempFiles &tempFiles, - CUpdateErrorInfo &errorInfo, - IUpdateCallbackUI2 *callback) -{ - for(int i = 0; i < options.Commands.Size(); i++) - { - CUpdateArchiveCommand &command = options.Commands[i]; - if (options.StdOutMode) - { - RINOK(callback->StartArchive(L"stdout", archive != 0)); - } - else - { - RINOK(callback->StartArchive(command.ArchivePath.GetFinalPath(), - i == 0 && options.UpdateArchiveItself && archive != 0)); - } +#include <mapi.h> - RINOK(Compress( - codecs, - command.ActionSet, archive, - options.MethodMode, - command.ArchivePath, - arcItems, - options.OpenShareForWrite, - options.StdInMode, - /* options.StdInFileName, */ - options.StdOutMode, - dirItems, - options.SfxMode, options.SfxModule, - options.VolumesSizes, - tempFiles, - errorInfo, callback)); - - RINOK(callback->FinishArchive()); - } - return S_OK; -} +#endif -#if defined(_WIN32) && !defined(UNDER_CE) -class CCurrentDirRestorer +struct CRefSortPair { - FString _path; -public: - CCurrentDirRestorer() { NFile::NDirectory::MyGetCurrentDirectory(_path); } - ~CCurrentDirRestorer() { RestoreDirectory();} - bool RestoreDirectory() const { return BOOLToBool(NFile::NDirectory::MySetCurrentDirectory(_path)); } + unsigned Len; + unsigned Index; }; -#endif -struct CEnumDirItemUpdateCallback: public IEnumDirItemCallback +#define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; } + +static int CompareRefSortPair(const CRefSortPair *a1, const CRefSortPair *a2, void *) +{ + RINOZ(-MyCompare(a1->Len, a2->Len)); + return MyCompare(a1->Index, a2->Index); +} + +static unsigned GetNumSlashes(const FChar *s) { - IUpdateCallbackUI2 *Callback; - HRESULT ScanProgress(UInt64 numFolders, UInt64 numFiles, const wchar_t *path) + for (unsigned numSlashes = 0;;) { - return Callback->ScanProgress(numFolders, numFiles, path); + FChar c = *s++; + if (c == 0) + return numSlashes; + if (IS_PATH_SEPAR(c)) + numSlashes++; } -}; +} #ifdef _WIN32 -typedef ULONG (FAR PASCAL MY_MAPISENDDOCUMENTS)( - ULONG_PTR ulUIParam, - LPSTR lpszDelimChar, - LPSTR lpszFilePaths, - LPSTR lpszFileNames, - ULONG ulReserved -); -typedef MY_MAPISENDDOCUMENTS FAR *MY_LPMAPISENDDOCUMENTS; +void ConvertToLongNames(NWildcard::CCensor &censor); #endif HRESULT UpdateArchive( CCodecs *codecs, - const NWildcard::CCensor &censor, + const CObjectVector<COpenType> &types, + const UString &cmdArcPath2, + NWildcard::CCensor &censor, CUpdateOptions &options, CUpdateErrorInfo &errorInfo, IOpenCallbackUI *openCallback, - IUpdateCallbackUI2 *callback) + IUpdateCallbackUI2 *callback, + bool needSetPath) { if (options.StdOutMode && options.EMailMode) return E_FAIL; - if (options.VolumesSizes.Size() > 0 && (options.EMailMode || options.SfxMode)) + if (types.Size() > 1) + return E_NOTIMPL; + + bool renameMode = !options.RenamePairs.IsEmpty(); + if (renameMode) + { + if (options.Commands.Size() != 1) + return E_FAIL; + } + + if (options.DeleteAfterCompressing) + { + if (options.Commands.Size() != 1) + return E_NOTIMPL; + const CActionSet &as = options.Commands[0].ActionSet; + for (int i = 2; i < NPairState::kNumValues; i++) + if (as.StateActions[i] != NPairAction::kCompress) + return E_NOTIMPL; + } + + censor.AddPathsToCensor(options.PathMode); + #ifdef _WIN32 + ConvertToLongNames(censor); + #endif + censor.ExtendExclude(); + + + if (options.VolumesSizes.Size() > 0 && (options.EMailMode /* || options.SfxMode */)) return E_NOTIMPL; if (options.SfxMode) { CProperty property; - property.Name = L"rsfx"; - property.Value = L"on"; + property.Name.SetFromAscii("rsfx"); options.MethodMode.Properties.Add(property); if (options.SfxModule.IsEmpty()) { - errorInfo.Message = L"SFX file is not specified"; + errorInfo.Message = "SFX file is not specified"; return E_FAIL; } bool found = false; @@ -663,57 +1047,133 @@ HRESULT UpdateArchive( if (!found) { if (!NFind::DoesFileExist(options.SfxModule)) - { - errorInfo.SystemError = ::GetLastError(); - errorInfo.Message = L"7-Zip cannot find specified SFX module"; - errorInfo.FileName = options.SfxModule; - return E_FAIL; - } + return errorInfo.SetFromLastError("cannot find specified SFX module", options.SfxModule); } } - CArchiveLink arcLink; - const UString arcPath = options.ArchivePath.GetFinalPath(); - if (!options.ArchivePath.OriginalPath.IsEmpty()) + + if (needSetPath) + { + if (!options.InitFormatIndex(codecs, types, cmdArcPath2) || + !options.SetArcPath(codecs, cmdArcPath2)) + return E_NOTIMPL; + } + UString arcPath = options.ArchivePath.GetFinalPath(); + + if (cmdArcPath2.IsEmpty()) + { + if (options.MethodMode.Type.FormatIndex < 0) + throw "type of archive is not specified"; + } + else { NFind::CFileInfo fi; - if (fi.Find(us2fs(arcPath))) + if (!fi.Find(us2fs(arcPath))) + { + if (renameMode) + throw "can't find archive";; + if (options.MethodMode.Type.FormatIndex < 0) + { + if (!options.SetArcPath(codecs, cmdArcPath2)) + return E_NOTIMPL; + } + } + else { if (fi.IsDir()) throw "there is no such archive"; + if (fi.IsDevice) + return E_NOTIMPL; if (options.VolumesSizes.Size() > 0) return E_NOTIMPL; - CIntVector formatIndices; - if (options.MethodMode.FormatIndex >= 0) - formatIndices.Add(options.MethodMode.FormatIndex); - HRESULT result = arcLink.Open2(codecs, formatIndices, false, NULL, arcPath, openCallback); + CObjectVector<COpenType> types; + // change it. + if (options.MethodMode.Type_Defined) + types.Add(options.MethodMode.Type); + // We need to set Properties to open archive only in some cases (WIM archives). + + CIntVector excl; + COpenOptions op; + #ifndef _SFX + op.props = &options.MethodMode.Properties; + #endif + op.codecs = codecs; + op.types = &types; + op.excludedFormats = ! + op.stdInMode = false; + op.stream = NULL; + op.filePath = arcPath; + + RINOK(callback->StartOpenArchive(arcPath)); + + HRESULT result = arcLink.Open3(op, openCallback); + if (result == E_ABORT) return result; - RINOK(callback->OpenResult(arcPath, result)); + + if (result == S_OK && arcLink.NonOpen_ErrorInfo.ErrorFormatIndex >= 0) + result = S_FALSE; + + HRESULT res2 = callback->OpenResult(codecs, arcLink, arcPath, result); + /* + if (result == S_FALSE) + return E_FAIL; + */ + RINOK(res2); RINOK(result); + if (arcLink.VolumePaths.Size() > 1) { errorInfo.SystemError = (DWORD)E_NOTIMPL; - errorInfo.Message = L"Updating for multivolume archives is not implemented"; + errorInfo.Message = "Updating for multivolume archives is not implemented"; return E_NOTIMPL; } CArc &arc = arcLink.Arcs.Back(); arc.MTimeDefined = !fi.IsDevice; arc.MTime = fi.MTime; + + if (arc.ErrorInfo.ThereIsTail) + { + errorInfo.SystemError = (DWORD)E_NOTIMPL; + errorInfo.Message = "There is some data block after the end of the archive"; + return E_NOTIMPL; + } + if (options.MethodMode.Type.FormatIndex < 0) + { + options.MethodMode.Type.FormatIndex = arcLink.GetArc()->FormatIndex; + if (!options.SetArcPath(codecs, cmdArcPath2)) + return E_NOTIMPL; + } } } - else + + if (options.MethodMode.Type.FormatIndex < 0) { - /* - if (archiveType.IsEmpty()) - throw "type of archive is not specified"; - */ + options.MethodMode.Type.FormatIndex = codecs->FindFormatForArchiveType(kDefaultArcType); + if (options.MethodMode.Type.FormatIndex < 0) + return E_NOTIMPL; } + bool thereIsInArchive = arcLink.IsOpen; + if (!thereIsInArchive && renameMode) + return E_FAIL; + CDirItems dirItems; + dirItems.Callback = callback; + + CDirItem parentDirItem; + CDirItem *parentDirItem_Ptr = NULL; + + /* + FStringVector requestedPaths; + FStringVector *requestedPaths_Ptr = NULL; + if (options.DeleteAfterCompressing) + requestedPaths_Ptr = &requestedPaths; + */ + if (options.StdInMode) { CDirItem di; @@ -727,28 +1187,70 @@ HRESULT UpdateArchive( else { bool needScanning = false; - for(int i = 0; i < options.Commands.Size(); i++) + + if (!renameMode) + FOR_VECTOR (i, options.Commands) if (options.Commands[i].ActionSet.NeedScanning()) needScanning = true; + if (needScanning) { - CEnumDirItemUpdateCallback enumCallback; - enumCallback.Callback = callback; RINOK(callback->StartScanning()); - FStringVector errorPaths; - CRecordVector<DWORD> errorCodes; - HRESULT res = EnumerateItems(censor, dirItems, &enumCallback, errorPaths, errorCodes); - for (int i = 0; i < errorPaths.Size(); i++) - { - RINOK(callback->CanNotFindError(fs2us(errorPaths[i]), errorCodes[i])); - } + + dirItems.SymLinks = options.SymLinks.Val; + + #if defined(_WIN32) && !defined(UNDER_CE) + dirItems.ReadSecure = options.NtSecurity.Val; + #endif + + dirItems.ScanAltStreams = options.AltStreams.Val; + + HRESULT res = EnumerateItems(censor, + options.PathMode, + options.AddPathPrefix, + dirItems); + if (res != S_OK) { if (res != E_ABORT) - errorInfo.Message = L"Scanning error"; + errorInfo.Message = "Scanning error"; return res; } - RINOK(callback->FinishScanning()); + + RINOK(callback->FinishScanning(dirItems.Stat)); + + if (censor.Pairs.Size() == 1) + { + NFind::CFileInfo fi; + FString prefix = us2fs(censor.Pairs[0].Prefix); + prefix += FTEXT('.'); + // UString prefix = censor.Pairs[0].Prefix; + /* + if (prefix.Back() == WCHAR_PATH_SEPARATOR) + { + prefix.DeleteBack(); + } + */ + if (fi.Find(prefix)) + if (fi.IsDir()) + { + parentDirItem.Size = fi.Size; + parentDirItem.CTime = fi.CTime; + parentDirItem.ATime = fi.ATime; + parentDirItem.MTime = fi.MTime; + parentDirItem.Attrib = fi.Attrib; + parentDirItem_Ptr = &parentDirItem; + + int secureIndex = -1; + #if defined(_WIN32) && !defined(UNDER_CE) + if (options.NtSecurity.Val) + dirItems.AddSecurityItem(prefix, secureIndex); + #endif + parentDirItem.SecureIndex = secureIndex; + + parentDirItem_Ptr = &parentDirItem; + } + } } } @@ -756,7 +1258,7 @@ HRESULT UpdateArchive( bool usesTempDir = false; #ifdef _WIN32 - NDirectory::CTempDir tempDirectory; + CTempDir tempDirectory; if (options.EMailMode && options.EMailRemoveAfter) { tempDirectory.Create(kTempFolderPrefix); @@ -770,8 +1272,6 @@ HRESULT UpdateArchive( bool createTempFile = false; - bool thereIsInArchive = arcLink.IsOpen; - if (!options.StdOutMode && options.UpdateArchiveItself) { CArchivePath &ap = options.Commands[0].ArchivePath; @@ -782,14 +1282,15 @@ HRESULT UpdateArchive( createTempFile = true; ap.Temp = true; if (!options.WorkingDir.IsEmpty()) - { ap.TempPrefix = options.WorkingDir; - NormalizeDirPathPrefix(ap.TempPrefix); - } + else + ap.TempPrefix = us2fs(ap.Prefix); + NormalizeDirPathPrefix(ap.TempPrefix); } } - for (int i = 0; i < options.Commands.Size(); i++) + unsigned i; + for (i = 0; i < options.Commands.Size(); i++) { CArchivePath &ap = options.Commands[i].ArchivePath; if (usesTempDir) @@ -805,10 +1306,10 @@ HRESULT UpdateArchive( const FString path = us2fs(ap.GetFinalPath()); if (NFind::DoesFileOrDirExist(path)) { - errorInfo.SystemError = 0; - errorInfo.Message = L"The file already exists"; - errorInfo.FileName = path; - return E_FAIL; + errorInfo.SystemError = ERROR_FILE_EXISTS; + errorInfo.Message = "The file already exists"; + errorInfo.FileNames.Add(path); + return errorInfo.Get_HRESULT_Error(); } } } @@ -816,13 +1317,77 @@ HRESULT UpdateArchive( CObjectVector<CArcItem> arcItems; if (thereIsInArchive) { - RINOK(EnumerateInArchiveItems(censor, arcLink.Arcs.Back(), arcItems)); + RINOK(EnumerateInArchiveItems( + // options.StoreAltStreams, + censor, arcLink.Arcs.Back(), arcItems)); + } + + /* + FStringVector processedFilePaths; + FStringVector *processedFilePaths_Ptr = NULL; + if (options.DeleteAfterCompressing) + processedFilePaths_Ptr = &processedFilePaths; + */ + + CByteBuffer processedItems; + if (options.DeleteAfterCompressing) + { + unsigned num = dirItems.Items.Size(); + processedItems.Alloc(num); + for (i = 0; i < num; i++) + processedItems[i] = 0; + } + + /* + #ifndef _NO_CRYPTO + if (arcLink.PasswordWasAsked) + { + // We set password, if open have requested password + RINOK(callback->SetPassword(arcLink.Password)); + } + #endif + */ + + for (i = 0; i < options.Commands.Size(); i++) + { + const CArc *arc = thereIsInArchive ? arcLink.GetArc() : NULL; + CUpdateArchiveCommand &command = options.Commands[i]; + UString name; + bool isUpdating; + + if (options.StdOutMode) + { + name.SetFromAscii("stdout"); + isUpdating = thereIsInArchive; + } + else + { + name = command.ArchivePath.GetFinalPath(); + isUpdating = (i == 0 && options.UpdateArchiveItself && thereIsInArchive); + } + + RINOK(callback->StartArchive(name, isUpdating)) + + CFinishArchiveStat st; + + RINOK(Compress(options, + isUpdating, + codecs, + command.ActionSet, + arc, + command.ArchivePath, + arcItems, + options.DeleteAfterCompressing ? (Byte *)processedItems : NULL, + + dirItems, + parentDirItem_Ptr, + + tempFiles, + errorInfo, callback, st)); + + RINOK(callback->FinishArchive(st)); } - RINOK(UpdateWithItemLists(codecs, options, - thereIsInArchive ? arcLink.GetArchive() : 0, - arcItems, dirItems, - tempFiles, errorInfo, callback)); if (thereIsInArchive) { @@ -837,21 +1402,16 @@ HRESULT UpdateArchive( { CArchivePath &ap = options.Commands[0].ArchivePath; const FString &tempPath = ap.GetTempPath(); + if (thereIsInArchive) - if (!NDirectory::DeleteFileAlways(us2fs(arcPath))) - { - errorInfo.SystemError = ::GetLastError(); - errorInfo.Message = L"7-Zip cannot delete the file"; - errorInfo.FileName = us2fs(arcPath); - return E_FAIL; - } - if (!NDirectory::MyMoveFile(tempPath, us2fs(arcPath))) + if (!DeleteFileAlways(us2fs(arcPath))) + return errorInfo.SetFromLastError("cannot delete the file", us2fs(arcPath)); + + if (!MyMoveFile(tempPath, us2fs(arcPath))) { - errorInfo.SystemError = ::GetLastError(); - errorInfo.Message = L"7-Zip cannot move the file"; - errorInfo.FileName = tempPath; - errorInfo.FileName2 = us2fs(arcPath); - return E_FAIL; + errorInfo.SetFromLastError("cannot move the file", tempPath); + errorInfo.FileNames.Add(us2fs(arcPath)); + return errorInfo.Get_HRESULT_Error(); } } catch(...) @@ -860,38 +1420,49 @@ HRESULT UpdateArchive( } } + #if defined(_WIN32) && !defined(UNDER_CE) + if (options.EMailMode) { NDLL::CLibrary mapiLib; if (!mapiLib.Load(FTEXT("Mapi32.dll"))) { - errorInfo.SystemError = ::GetLastError(); - errorInfo.Message = L"7-Zip cannot load Mapi32.dll"; - return E_FAIL; + errorInfo.SetFromLastError("cannot load Mapi32.dll"); + return errorInfo.Get_HRESULT_Error(); } - MY_LPMAPISENDDOCUMENTS fnSend = (MY_LPMAPISENDDOCUMENTS)mapiLib.GetProc("MAPISendDocuments"); + + /* + LPMAPISENDDOCUMENTS fnSend = (LPMAPISENDDOCUMENTS)mapiLib.GetProc("MAPISendDocuments"); if (fnSend == 0) { - errorInfo.SystemError = ::GetLastError(); - errorInfo.Message = L"7-Zip cannot find MAPISendDocuments function"; - return E_FAIL; + errorInfo.SetFromLastError)("7-Zip cannot find MAPISendDocuments function"); + return errorInfo.Get_HRESULT_Error(); + } + */ + + LPMAPISENDMAIL sendMail = (LPMAPISENDMAIL)mapiLib.GetProc("MAPISendMail"); + if (sendMail == 0) + { + errorInfo.SetFromLastError("7-Zip cannot find MAPISendMail function"); + return errorInfo.Get_HRESULT_Error();; } + FStringVector fullPaths; - int i; + unsigned i; + for (i = 0; i < options.Commands.Size(); i++) { CArchivePath &ap = options.Commands[i].ArchivePath; + FString finalPath = us2fs(ap.GetFinalPath()); FString arcPath; - if (!NFile::NDirectory::MyGetFullPathName(us2fs(ap.GetFinalPath()), arcPath)) - { - errorInfo.SystemError = ::GetLastError(); - errorInfo.Message = L"GetFullPathName error"; - return E_FAIL; - } + if (!MyGetFullPathName(finalPath, arcPath)) + return errorInfo.SetFromLastError("GetFullPathName error", finalPath); fullPaths.Add(arcPath); } + CCurrentDirRestorer curDirRestorer; + for (i = 0; i < fullPaths.Size(); i++) { UString arcPath = fs2us(fullPaths[i]); @@ -899,9 +1470,86 @@ HRESULT UpdateArchive( AString path = GetAnsiString(arcPath); AString name = GetAnsiString(fileName); // Warning!!! MAPISendDocuments function changes Current directory - fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0); + // fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0); + + MapiFileDesc f; + memset(&f, 0, sizeof(f)); + f.nPosition = 0xFFFFFFFF; + f.lpszPathName = (char *)(const char *)path; + f.lpszFileName = (char *)(const char *)name; + + MapiMessage m; + memset(&m, 0, sizeof(m)); + m.nFileCount = 1; + m.lpFiles = &f; + + const AString addr = GetAnsiString(options.EMailAddress); + MapiRecipDesc rec; + if (!addr.IsEmpty()) + { + memset(&rec, 0, sizeof(rec)); + rec.ulRecipClass = MAPI_TO; + rec.lpszAddress = (char *)(const char *)addr; + m.nRecipCount = 1; + m.lpRecips = &rec; + } + + sendMail((LHANDLE)0, 0, &m, MAPI_DIALOG, 0); } } + #endif + + if (options.DeleteAfterCompressing) + { + CRecordVector<CRefSortPair> pairs; + FStringVector foldersNames; + + for (i = 0; i < dirItems.Items.Size(); i++) + { + const CDirItem &dirItem = dirItems.Items[i]; + FString phyPath = dirItems.GetPhyPath(i); + if (dirItem.IsDir()) + { + CRefSortPair pair; + pair.Index = i; + pair.Len = GetNumSlashes(phyPath); + pairs.Add(pair); + } + else + { + if (processedItems[i] != 0 || dirItem.Size == 0) + { + RINOK(callback->DeletingAfterArchiving(phyPath, false)); + DeleteFileAlways(phyPath); + } + else + { + // file was skipped + /* + errorInfo.SystemError = 0; + errorInfo.Message = "file was not processed"; + errorInfo.FileName = phyPath; + return E_FAIL; + */ + } + } + } + + pairs.Sort(CompareRefSortPair, NULL); + + for (i = 0; i < pairs.Size(); i++) + { + FString phyPath = dirItems.GetPhyPath(pairs[i].Index); + if (NFind::DoesDirExist(phyPath)) + { + RINOK(callback->DeletingAfterArchiving(phyPath, true)); + RemoveDir(phyPath); + } + } + + RINOK(callback->FinishDeletingAfterArchiving()); + } + return S_OK; } diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/Update.h b/3rdparty/lzma/CPP/7zip/UI/Common/Update.h index 43a3c0fdb83..0301ae7660e 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/Update.h +++ b/3rdparty/lzma/CPP/7zip/UI/Common/Update.h @@ -3,14 +3,24 @@ #ifndef __COMMON_UPDATE_H #define __COMMON_UPDATE_H -#include "Common/Wildcard.h" +#include "../../../Common/Wildcard.h" #include "ArchiveOpenCallback.h" #include "LoadCodecs.h" +#include "OpenArchive.h" #include "Property.h" #include "UpdateAction.h" #include "UpdateCallback.h" +#include "DirItem.h" + +enum EArcNameMode +{ + k_ArcNameMode_Smart, + k_ArcNameMode_Exact, + k_ArcNameMode_Add, +}; + struct CArchivePath { UString OriginalPath; @@ -26,52 +36,11 @@ struct CArchivePath CArchivePath(): Temp(false) {}; - void ParseFromPath(const UString &path) - { - OriginalPath = path; - - SplitPathToParts(path, Prefix, Name); - int dotPos = Name.ReverseFind(L'.'); - if (dotPos < 0) - return; - if (dotPos == Name.Length() - 1) - { - Name = Name.Left(dotPos); - BaseExtension.Empty(); - return; - } - if (BaseExtension.CompareNoCase(Name.Mid(dotPos + 1)) == 0) - { - BaseExtension = Name.Mid(dotPos + 1); - Name = Name.Left(dotPos); - } - else - BaseExtension.Empty(); - } - - UString GetPathWithoutExt() const - { - return Prefix + Name; - } - - UString GetFinalPath() const - { - UString path = GetPathWithoutExt(); - if (!BaseExtension.IsEmpty()) - path += UString(L'.') + BaseExtension; - return path; - } - - - FString GetTempPath() const - { - FString path = TempPrefix + us2fs(Name); - if (!BaseExtension.IsEmpty()) - path += FString(FTEXT('.')) + us2fs(BaseExtension); - path += FTEXT(".tmp"); - path += TempPostfix; - return path; - } + void ParseFromPath(const UString &path, EArcNameMode mode); + UString GetPathWithoutExt() const { return Prefix + Name; } + UString GetFinalPath() const; + UString GetFinalVolPath() const; + FString GetTempPath() const; }; struct CUpdateArchiveCommand @@ -83,9 +52,31 @@ struct CUpdateArchiveCommand struct CCompressionMethodMode { - int FormatIndex; + bool Type_Defined; + COpenType Type; CObjectVector<CProperty> Properties; - CCompressionMethodMode(): FormatIndex(-1) {} + + CCompressionMethodMode(): Type_Defined(false) {} +}; + +namespace NRecursedType { enum EEnum +{ + kRecursed, + kWildcardOnlyRecursed, + kNonRecursed +};} + +struct CRenamePair +{ + UString OldName; + UString NewName; + bool WildcardParsing; + NRecursedType::EEnum RecursedType; + + CRenamePair(): WildcardParsing(true), RecursedType(NRecursedType::kNonRecursed) {} + + bool Prepare(); + bool GetNewPath(bool isFolder, const UString &src, UString &dest) const; }; struct CUpdateOptions @@ -95,7 +86,8 @@ struct CUpdateOptions CObjectVector<CUpdateArchiveCommand> Commands; bool UpdateArchiveItself; CArchivePath ArchivePath; - + EArcNameMode ArcNameMode; + bool SfxMode; FString SfxModule; @@ -110,8 +102,22 @@ struct CUpdateOptions UString EMailAddress; FString WorkingDir; + NWildcard::ECensorPathMode PathMode; + UString AddPathPrefix; - bool Init(const CCodecs *codecs, const CIntVector &formatIndices, const UString &arcPath); + CBoolPair NtSecurity; + CBoolPair AltStreams; + CBoolPair HardLinks; + CBoolPair SymLinks; + + bool DeleteAfterCompressing; + + bool SetArcMTime; + + CObjectVector<CRenamePair> RenamePairs; + + bool InitFormatIndex(const CCodecs *codecs, const CObjectVector<COpenType> &types, const UString &arcPath); + bool SetArcPath(const CCodecs *codecs, const UString &arcPath); CUpdateOptions(): UpdateArchiveItself(true), @@ -120,56 +126,73 @@ struct CUpdateOptions StdOutMode(false), EMailMode(false), EMailRemoveAfter(false), - OpenShareForWrite(false) + OpenShareForWrite(false), + ArcNameMode(k_ArcNameMode_Smart), + PathMode(NWildcard::k_RelatPath), + + DeleteAfterCompressing(false), + SetArcMTime(false) + {}; - void SetAddActionCommand() + void SetActionCommand_Add() { Commands.Clear(); CUpdateArchiveCommand c; - c.ActionSet = NUpdateArchive::kAddActionSet; + c.ActionSet = NUpdateArchive::k_ActionSet_Add; Commands.Add(c); } CRecordVector<UInt64> VolumesSizes; }; -struct CErrorInfo +struct CUpdateErrorInfo { DWORD SystemError; - FString FileName; - FString FileName2; - UString Message; - // UStringVector ErrorPaths; - // CRecordVector<DWORD> ErrorCodes; - CErrorInfo(): SystemError(0) {}; + AString Message; + FStringVector FileNames; + + bool ThereIsError() const { return SystemError != 0 || !Message.IsEmpty() || !FileNames.IsEmpty(); } + HRESULT Get_HRESULT_Error() const { return SystemError == 0 ? E_FAIL : HRESULT_FROM_WIN32(SystemError); } + void SetFromLastError(const char *message); + HRESULT SetFromLastError(const char *message, const FString &fileName); + + CUpdateErrorInfo(): SystemError(0) {}; }; -struct CUpdateErrorInfo: public CErrorInfo +struct CFinishArchiveStat { + UInt64 OutArcFileSize; + + CFinishArchiveStat(): OutArcFileSize(0) {} }; #define INTERFACE_IUpdateCallbackUI2(x) \ INTERFACE_IUpdateCallbackUI(x) \ - virtual HRESULT OpenResult(const wchar_t *name, HRESULT result) x; \ + INTERFACE_IDirItemsCallback(x) \ + virtual HRESULT OpenResult(const CCodecs *codecs, const CArchiveLink &arcLink, const wchar_t *name, HRESULT result) x; \ virtual HRESULT StartScanning() x; \ - virtual HRESULT ScanProgress(UInt64 numFolders, UInt64 numFiles, const wchar_t *path) x; \ - virtual HRESULT CanNotFindError(const wchar_t *name, DWORD systemError) x; \ - virtual HRESULT FinishScanning() x; \ + virtual HRESULT FinishScanning(const CDirItemsStat &st) x; \ + virtual HRESULT StartOpenArchive(const wchar_t *name) x; \ virtual HRESULT StartArchive(const wchar_t *name, bool updating) x; \ - virtual HRESULT FinishArchive() x; \ + virtual HRESULT FinishArchive(const CFinishArchiveStat &st) x; \ + virtual HRESULT DeletingAfterArchiving(const FString &path, bool isDir) x; \ + virtual HRESULT FinishDeletingAfterArchiving() x; \ -struct IUpdateCallbackUI2: public IUpdateCallbackUI +struct IUpdateCallbackUI2: public IUpdateCallbackUI, public IDirItemsCallback { INTERFACE_IUpdateCallbackUI2(=0) }; HRESULT UpdateArchive( CCodecs *codecs, - const NWildcard::CCensor &censor, + const CObjectVector<COpenType> &types, + const UString &cmdArcPath2, + NWildcard::CCensor &censor, CUpdateOptions &options, CUpdateErrorInfo &errorInfo, IOpenCallbackUI *openCallback, - IUpdateCallbackUI2 *callback); + IUpdateCallbackUI2 *callback, + bool needSetPath); #endif diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/UpdateAction.cpp b/3rdparty/lzma/CPP/7zip/UI/Common/UpdateAction.cpp index 879a49c57db..a80db7212e1 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/UpdateAction.cpp +++ b/3rdparty/lzma/CPP/7zip/UI/Common/UpdateAction.cpp @@ -6,7 +6,7 @@ namespace NUpdateArchive { -const CActionSet kAddActionSet = +const CActionSet k_ActionSet_Add = {{ NPairAction::kCopy, NPairAction::kCopy, @@ -17,7 +17,7 @@ const CActionSet kAddActionSet = NPairAction::kCompress }}; -const CActionSet kUpdateActionSet = +const CActionSet k_ActionSet_Update = {{ NPairAction::kCopy, NPairAction::kCopy, @@ -28,7 +28,7 @@ const CActionSet kUpdateActionSet = NPairAction::kCompress }}; -const CActionSet kFreshActionSet = +const CActionSet k_ActionSet_Fresh = {{ NPairAction::kCopy, NPairAction::kCopy, @@ -39,7 +39,7 @@ const CActionSet kFreshActionSet = NPairAction::kCompress }}; -const CActionSet kSynchronizeActionSet = +const CActionSet k_ActionSet_Sync = {{ NPairAction::kCopy, NPairAction::kIgnore, @@ -50,7 +50,7 @@ const CActionSet kSynchronizeActionSet = NPairAction::kCompress, }}; -const CActionSet kDeleteActionSet = +const CActionSet k_ActionSet_Delete = {{ NPairAction::kCopy, NPairAction::kIgnore, diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/UpdateAction.h b/3rdparty/lzma/CPP/7zip/UI/Common/UpdateAction.h index 0ac1c1080e7..bc53fcdbbb1 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/UpdateAction.h +++ b/3rdparty/lzma/CPP/7zip/UI/Common/UpdateAction.h @@ -7,7 +7,7 @@ namespace NUpdateArchive { namespace NPairState { - const int kNumValues = 7; + const unsigned kNumValues = 7; enum EEnum { kNotMasked = 0, @@ -34,9 +34,18 @@ namespace NUpdateArchive { struct CActionSet { NPairAction::EEnum StateActions[NPairState::kNumValues]; + + bool IsEqualTo(const CActionSet &a) const + { + for (unsigned i = 0; i < NPairState::kNumValues; i++) + if (StateActions[i] != a.StateActions[i]) + return false; + return true; + } + bool NeedScanning() const { - int i; + unsigned i; for (i = 0; i < NPairState::kNumValues; i++) if (StateActions[i] == NPairAction::kCompress) return true; @@ -47,11 +56,11 @@ namespace NUpdateArchive { } }; - extern const CActionSet kAddActionSet; - extern const CActionSet kUpdateActionSet; - extern const CActionSet kFreshActionSet; - extern const CActionSet kSynchronizeActionSet; - extern const CActionSet kDeleteActionSet; + extern const CActionSet k_ActionSet_Add; + extern const CActionSet k_ActionSet_Update; + extern const CActionSet k_ActionSet_Fresh; + extern const CActionSet k_ActionSet_Sync; + extern const CActionSet k_ActionSet_Delete; } #endif diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/UpdateCallback.cpp b/3rdparty/lzma/CPP/7zip/UI/Common/UpdateCallback.cpp index 21146bb0a2d..6c439cfd78d 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/UpdateCallback.cpp +++ b/3rdparty/lzma/CPP/7zip/UI/Common/UpdateCallback.cpp @@ -2,29 +2,70 @@ #include "StdAfx.h" -#include "Common/ComTry.h" -#include "Common/Defs.h" -#include "Common/IntToString.h" -#include "Common/StringConvert.h" +#ifndef _7ZIP_ST +#include "../../../Windows/Synchronization.h" +#endif -#include "Windows/PropVariant.h" +#include "../../../Common/ComTry.h" +#include "../../../Common/IntToString.h" +#include "../../../Common/StringConvert.h" +#include "../../../Common/Wildcard.h" -#include "../../Common/FileStreams.h" +#include "../../../Windows/FileDir.h" +#include "../../../Windows/FileName.h" +#include "../../../Windows/PropVariant.h" + +#include "../../Common/StreamObjects.h" #include "UpdateCallback.h" +#if defined(_WIN32) && !defined(UNDER_CE) +#define _USE_SECURITY_CODE +#include "../../../Windows/SecurityUtils.h" +#endif + using namespace NWindows; +using namespace NFile; + +#ifndef _7ZIP_ST +static NSynchronization::CCriticalSection g_CriticalSection; +#define MT_LOCK NSynchronization::CCriticalSectionLock lock(g_CriticalSection); +#else +#define MT_LOCK +#endif + + +#ifdef _USE_SECURITY_CODE +bool InitLocalPrivileges(); +#endif CArchiveUpdateCallback::CArchiveUpdateCallback(): - Callback(0), - ShareForWrite(false), - StdInMode(false), - DirItems(0), - ArcItems(0), - UpdatePairs(0), - NewNames(0), - KeepOriginalItemNames(0) - {} + _hardIndex_From((UInt32)(Int32)-1), + + Callback(NULL), + + DirItems(NULL), + ParentDirItem(NULL), + + Arc(NULL), + ArcItems(NULL), + UpdatePairs(NULL), + NewNames(NULL), + + ShareForWrite(false), + StdInMode(false), + + KeepOriginalItemNames(false), + StoreNtSecurity(false), + StoreHardLinks(false), + StoreSymLinks(false), + + ProcessedItemsStatuses(NULL) +{ + #ifdef _USE_SECURITY_CODE + _saclEnabled = InitLocalPrivileges(); + #endif +} STDMETHODIMP CArchiveUpdateCallback::SetTotal(UInt64 size) @@ -50,7 +91,7 @@ STDMETHODIMP CArchiveUpdateCallback::SetRatioInfo(const UInt64 *inSize, const UI /* -STATPROPSTG kProperties[] = +static const CStatProp kProps[] = { { NULL, kpidPath, VT_BSTR}, { NULL, kpidIsDir, VT_BOOL}, @@ -64,7 +105,7 @@ STATPROPSTG kProperties[] = STDMETHODIMP CArchiveUpdateCallback::EnumProperties(IEnumSTATPROPSTG **) { - return CStatPropEnumerator::CreateEnumerator(kProperties, sizeof(kProperties) / sizeof(kProperties[0]), enumerator); + return CStatPropEnumerator::CreateEnumerator(kProps, ARRAY_SIZE(kProps), enumerator); } */ @@ -74,11 +115,11 @@ STDMETHODIMP CArchiveUpdateCallback::GetUpdateItemInfo(UInt32 index, COM_TRY_BEGIN RINOK(Callback->CheckBreak()); const CUpdatePair2 &up = (*UpdatePairs)[index]; - if (newData != NULL) *newData = BoolToInt(up.NewData); - if (newProps != NULL) *newProps = BoolToInt(up.NewProps); - if (indexInArchive != NULL) + if (newData) *newData = BoolToInt(up.NewData); + if (newProps) *newProps = BoolToInt(up.NewProps); + if (indexInArchive) { - *indexInArchive = (UInt32)-1; + *indexInArchive = (UInt32)(Int32)-1; if (up.ExistInArchive()) *indexInArchive = (ArcItems == 0) ? up.ArcIndex : (*ArcItems)[up.ArcIndex].IndexInServer; } @@ -86,84 +127,300 @@ STDMETHODIMP CArchiveUpdateCallback::GetUpdateItemInfo(UInt32 index, COM_TRY_END } -STDMETHODIMP CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +STDMETHODIMP CArchiveUpdateCallback::GetRootProp(PROPID propID, PROPVARIANT *value) { - COM_TRY_BEGIN - const CUpdatePair2 &up = (*UpdatePairs)[index]; - NWindows::NCOM::CPropVariant prop; - - if (propID == kpidIsAnti) + NCOM::CPropVariant prop; + switch (propID) { - prop = up.IsAnti; - prop.Detach(value); - return S_OK; + case kpidIsDir: prop = true; break; + case kpidAttrib: if (ParentDirItem) prop = ParentDirItem->Attrib; break; + case kpidCTime: if (ParentDirItem) prop = ParentDirItem->CTime; break; + case kpidATime: if (ParentDirItem) prop = ParentDirItem->ATime; break; + case kpidMTime: if (ParentDirItem) prop = ParentDirItem->MTime; break; } + prop.Detach(value); + return S_OK; +} - if (up.IsAnti) +STDMETHODIMP CArchiveUpdateCallback::GetParent(UInt32 /* index */, UInt32 *parent, UInt32 *parentType) +{ + *parentType = NParentType::kDir; + *parent = (UInt32)(Int32)-1; + return S_OK; +} + +STDMETHODIMP CArchiveUpdateCallback::GetNumRawProps(UInt32 *numProps) +{ + *numProps = 0; + if (StoreNtSecurity) + *numProps = 1; + return S_OK; +} + +STDMETHODIMP CArchiveUpdateCallback::GetRawPropInfo(UInt32 /* index */, BSTR *name, PROPID *propID) +{ + *name = NULL; + *propID = kpidNtSecure; + return S_OK; +} + +STDMETHODIMP CArchiveUpdateCallback::GetRootRawProp(PROPID + #ifdef _USE_SECURITY_CODE + propID + #endif + , const void **data, UInt32 *dataSize, UInt32 *propType) +{ + *data = 0; + *dataSize = 0; + *propType = 0; + if (!StoreNtSecurity) + return S_OK; + #ifdef _USE_SECURITY_CODE + if (propID == kpidNtSecure) { - switch(propID) + if (StdInMode) + return S_OK; + + if (ParentDirItem) { - case kpidIsDir: - case kpidPath: - break; - case kpidSize: - prop = (UInt64)0; - prop.Detach(value); + if (ParentDirItem->SecureIndex < 0) return S_OK; - default: - prop.Detach(value); + const CByteBuffer &buf = DirItems->SecureBlocks.Bufs[ParentDirItem->SecureIndex]; + *data = buf; + *dataSize = (UInt32)buf.Size(); + *propType = NPropDataType::kRaw; + return S_OK; + } + + if (Arc && Arc->GetRootProps) + return Arc->GetRootProps->GetRootRawProp(propID, data, dataSize, propType); + } + #endif + return S_OK; +} + +// #ifdef _USE_SECURITY_CODE +// #endif + +STDMETHODIMP CArchiveUpdateCallback::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType) +{ + *data = 0; + *dataSize = 0; + *propType = 0; + + if (propID == kpidNtSecure || + propID == kpidNtReparse) + { + if (StdInMode) + return S_OK; + + const CUpdatePair2 &up = (*UpdatePairs)[index]; + if (up.UseArcProps && up.ExistInArchive() && Arc->GetRawProps) + return Arc->GetRawProps->GetRawProp( + ArcItems ? (*ArcItems)[up.ArcIndex].IndexInServer : up.ArcIndex, + propID, data, dataSize, propType); + + { + const CUpdatePair2 &up = (*UpdatePairs)[index]; + /* + if (!up.NewData) + return E_FAIL; + */ + if (up.IsAnti) return S_OK; + + #ifndef UNDER_CE + const CDirItem &di = DirItems->Items[up.DirIndex]; + #endif + + #ifdef _USE_SECURITY_CODE + if (propID == kpidNtSecure) + { + if (!StoreNtSecurity) + return S_OK; + if (di.SecureIndex < 0) + return S_OK; + const CByteBuffer &buf = DirItems->SecureBlocks.Bufs[di.SecureIndex]; + *data = buf; + *dataSize = (UInt32)buf.Size(); + *propType = NPropDataType::kRaw; + } + else + #endif + { + // propID == kpidNtReparse + if (!StoreSymLinks) + return S_OK; + #ifndef UNDER_CE + const CByteBuffer *buf = &di.ReparseData2; + if (buf->Size() == 0) + buf = &di.ReparseData; + if (buf->Size() != 0) + { + *data = *buf; + *dataSize = (UInt32)buf->Size(); + *propType = NPropDataType::kRaw; + } + #endif + } + + return S_OK; } } + + return S_OK; +} + +#ifndef UNDER_CE + +static UString GetRelativePath(const UString &to, const UString &from) +{ + UStringVector partsTo, partsFrom; + SplitPathToParts(to, partsTo); + SplitPathToParts(from, partsFrom); + + unsigned i; + for (i = 0;; i++) + { + if (i + 1 >= partsFrom.Size() || + i + 1 >= partsTo.Size()) + break; + if (CompareFileNames(partsFrom[i], partsTo[i]) != 0) + break; + } + + if (i == 0) + { + #ifdef _WIN32 + if (NName::IsDrivePath(to) || + NName::IsDrivePath(from)) + return to; + #endif + } + + UString s; + unsigned k; + + for (k = i + 1; k < partsFrom.Size(); k++) + s += L".." WSTRING_PATH_SEPARATOR; - if (up.ExistOnDisk()) + for (k = i; k < partsTo.Size(); k++) { - const CDirItem &di = DirItems->Items[up.DirIndex]; - switch(propID) + if (k != i) + s.Add_PathSepar(); + s += partsTo[k]; + } + + return s; +} + +#endif + +STDMETHODIMP CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + const CUpdatePair2 &up = (*UpdatePairs)[index]; + NCOM::CPropVariant prop; + + if (up.NewData) + { + /* + if (propID == kpidIsHardLink) + { + prop = _isHardLink; + prop.Detach(value); + return S_OK; + } + */ + if (propID == kpidSymLink) { - case kpidPath: + if (index == _hardIndex_From) + { + prop.Detach(value); + return S_OK; + } + if (up.DirIndex >= 0) + { + #ifndef UNDER_CE + const CDirItem &di = DirItems->Items[up.DirIndex]; + // if (di.IsDir()) { - if (KeepOriginalItemNames) + CReparseAttr attr; + if (attr.Parse(di.ReparseData, di.ReparseData.Size())) { - if (up.ExistInArchive() && Archive) + UString simpleName = attr.GetPath(); + if (attr.IsRelative()) + prop = simpleName; + else { - UInt32 indexInArchive; - if (ArcItems == 0) - indexInArchive = up.ArcIndex; - else - indexInArchive = (*ArcItems)[up.ArcIndex].IndexInServer; - return Archive->GetProperty(indexInArchive, propID, value); + const FString phyPath = DirItems->GetPhyPath(up.DirIndex); + FString fullPath; + if (NDir::MyGetFullPathName(phyPath, fullPath)) + { + prop = GetRelativePath(simpleName, fs2us(fullPath)); + } } + prop.Detach(value); + return S_OK; } - prop = DirItems->GetLogPath(up.DirIndex); break; } - case kpidIsDir: prop = di.IsDir(); break; - case kpidSize: prop = di.Size; break; - case kpidAttrib: prop = di.Attrib; break; - case kpidCTime: prop = di.CTime; break; - case kpidATime: prop = di.ATime; break; - case kpidMTime: prop = di.MTime; break; + #endif + } } - } - else - { - if (propID == kpidPath) + else if (propID == kpidHardLink) { - if (up.NewNameIndex >= 0) + if (index == _hardIndex_From) + { + const CKeyKeyValPair &pair = _map[_hardIndex_To]; + const CUpdatePair2 &up2 = (*UpdatePairs)[pair.Value]; + prop = DirItems->GetLogPath(up2.DirIndex); + prop.Detach(value); + return S_OK; + } + if (up.DirIndex >= 0) { - prop = (*NewNames)[up.NewNameIndex]; prop.Detach(value); return S_OK; } } - if (up.ExistInArchive() && Archive) + } + + if (up.IsAnti + && propID != kpidIsDir + && propID != kpidPath + && propID != kpidIsAltStream) + { + switch (propID) { - UInt32 indexInArchive; - if (ArcItems == 0) - indexInArchive = up.ArcIndex; - else - indexInArchive = (*ArcItems)[up.ArcIndex].IndexInServer; - return Archive->GetProperty(indexInArchive, propID, value); + case kpidSize: prop = (UInt64)0; break; + case kpidIsAnti: prop = true; break; + } + } + else if (propID == kpidPath && up.NewNameIndex >= 0) + prop = (*NewNames)[up.NewNameIndex]; + else if (propID == kpidShortName && up.NewNameIndex >= 0 && up.IsMainRenameItem) + { + // we can generate new ShortName here; + } + else if ((up.UseArcProps || (KeepOriginalItemNames && (propID == kpidPath || propID == kpidIsAltStream))) + && up.ExistInArchive() && Archive) + return Archive->GetProperty(ArcItems ? (*ArcItems)[up.ArcIndex].IndexInServer : up.ArcIndex, propID, value); + else if (up.ExistOnDisk()) + { + const CDirItem &di = DirItems->Items[up.DirIndex]; + switch (propID) + { + case kpidPath: prop = DirItems->GetLogPath(up.DirIndex); break; + case kpidIsDir: prop = di.IsDir(); break; + case kpidSize: prop = di.IsDir() ? (UInt64)0 : di.Size; break; + case kpidAttrib: prop = di.Attrib; break; + case kpidCTime: prop = di.CTime; break; + case kpidATime: prop = di.ATime; break; + case kpidMTime: prop = di.MTime; break; + case kpidIsAltStream: prop = di.IsAltStream; break; + #if defined(_WIN32) && !defined(UNDER_CE) + // case kpidShortName: prop = di.ShortName; break; + #endif } } prop.Detach(value); @@ -171,28 +428,56 @@ STDMETHODIMP CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PR COM_TRY_END } -STDMETHODIMP CArchiveUpdateCallback::GetStream(UInt32 index, ISequentialInStream **inStream) +#ifndef _7ZIP_ST +static NSynchronization::CCriticalSection CS; +#endif + +STDMETHODIMP CArchiveUpdateCallback::GetStream2(UInt32 index, ISequentialInStream **inStream, UInt32 mode) { COM_TRY_BEGIN + *inStream = NULL; const CUpdatePair2 &up = (*UpdatePairs)[index]; if (!up.NewData) return E_FAIL; RINOK(Callback->CheckBreak()); - RINOK(Callback->Finilize()); + // RINOK(Callback->Finalize()); + + bool isDir = IsDir(up); if (up.IsAnti) { - return Callback->GetStream((*ArcItems)[up.ArcIndex].Name, true); + UString name; + if (up.ArcIndex >= 0) + name = (*ArcItems)[up.ArcIndex].Name; + else if (up.DirIndex >= 0) + name = DirItems->GetLogPath(up.DirIndex); + RINOK(Callback->GetStream(name, isDir, true, mode)); + + /* 9.33: fixed. Handlers expect real stream object for files, even for anti-file. + so we return empty stream */ + + if (!isDir) + { + CBufInStream *inStreamSpec = new CBufInStream(); + CMyComPtr<ISequentialInStream> inStreamLoc = inStreamSpec; + inStreamSpec->Init(NULL, 0); + *inStream = inStreamLoc.Detach(); + } + return S_OK; } - const CDirItem &di = DirItems->Items[up.DirIndex]; - RINOK(Callback->GetStream(DirItems->GetLogPath(up.DirIndex), false)); + + RINOK(Callback->GetStream(DirItems->GetLogPath(up.DirIndex), isDir, false, mode)); - if (di.IsDir()) + if (isDir) return S_OK; if (StdInMode) { + if (mode != NUpdateNotifyOp::kAdd && + mode != NUpdateNotifyOp::kUpdate) + return S_OK; + CStdInFileStream *inStreamSpec = new CStdInFileStream; CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec); *inStream = inStreamLoc.Detach(); @@ -201,21 +486,196 @@ STDMETHODIMP CArchiveUpdateCallback::GetStream(UInt32 index, ISequentialInStream { CInFileStream *inStreamSpec = new CInFileStream; CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec); - const UString path = DirItems->GetPhyPath(up.DirIndex); - if (!inStreamSpec->OpenShared(us2fs(path), ShareForWrite)) + + inStreamSpec->SupportHardLinks = StoreHardLinks; + inStreamSpec->Callback = this; + inStreamSpec->CallbackRef = index; + + const FString path = DirItems->GetPhyPath(up.DirIndex); + _openFiles_Indexes.Add(index); + _openFiles_Paths.Add(path); + + #if defined(_WIN32) && !defined(UNDER_CE) + if (DirItems->Items[up.DirIndex].AreReparseData()) + { + if (!inStreamSpec->File.OpenReparse(path)) + { + return Callback->OpenFileError(path, ::GetLastError()); + } + } + else + #endif + if (!inStreamSpec->OpenShared(path, ShareForWrite)) { return Callback->OpenFileError(path, ::GetLastError()); } + + if (StoreHardLinks) + { + CStreamFileProps props; + if (inStreamSpec->GetProps2(&props) == S_OK) + { + if (props.NumLinks > 1) + { + CKeyKeyValPair pair; + pair.Key1 = props.VolID; + pair.Key2 = props.FileID_Low; + pair.Value = index; + unsigned numItems = _map.Size(); + unsigned pairIndex = _map.AddToUniqueSorted2(pair); + if (numItems == _map.Size()) + { + // const CKeyKeyValPair &pair2 = _map.Pairs[pairIndex]; + _hardIndex_From = index; + _hardIndex_To = pairIndex; + // we could return NULL as stream, but it's better to return real stream + // return S_OK; + } + } + } + } + + if (ProcessedItemsStatuses) + { + #ifndef _7ZIP_ST + NSynchronization::CCriticalSectionLock lock(CS); + #endif + ProcessedItemsStatuses[(unsigned)up.DirIndex] = 1; + } *inStream = inStreamLoc.Detach(); } + return S_OK; COM_TRY_END } -STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 operationResult) +STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 opRes) +{ + COM_TRY_BEGIN + return Callback->SetOperationResult(opRes); + COM_TRY_END +} + +STDMETHODIMP CArchiveUpdateCallback::GetStream(UInt32 index, ISequentialInStream **inStream) { COM_TRY_BEGIN - return Callback->SetOperationResult(operationResult); + return GetStream2(index, inStream, + (*UpdatePairs)[index].ArcIndex < 0 ? + NUpdateNotifyOp::kAdd : + NUpdateNotifyOp::kUpdate); + COM_TRY_END +} + +STDMETHODIMP CArchiveUpdateCallback::ReportOperation(UInt32 indexType, UInt32 index, UInt32 op) +{ + COM_TRY_BEGIN + + bool isDir = false; + + if (indexType == NArchive::NEventIndexType::kOutArcIndex) + { + UString name; + if (index != (UInt32)(Int32)-1) + { + const CUpdatePair2 &up = (*UpdatePairs)[index]; + if (up.ExistOnDisk()) + { + name = DirItems->GetLogPath(up.DirIndex); + isDir = DirItems->Items[up.DirIndex].IsDir(); + } + } + return Callback->ReportUpdateOpeartion(op, name.IsEmpty() ? NULL : name.Ptr(), isDir); + } + + wchar_t temp[16]; + UString s2; + const wchar_t *s = NULL; + + if (indexType == NArchive::NEventIndexType::kInArcIndex) + { + if (index != (UInt32)(Int32)-1) + { + if (ArcItems) + { + const CArcItem &ai = (*ArcItems)[index]; + s = ai.Name; + isDir = ai.IsDir; + } + else if (Arc) + { + RINOK(Arc->GetItemPath(index, s2)); + s = s2; + RINOK(Archive_IsItem_Dir(Arc->Archive, index, isDir)); + } + } + } + else if (indexType == NArchive::NEventIndexType::kBlockIndex) + { + temp[0] = '#'; + ConvertUInt32ToString(index, temp + 1); + s = temp; + } + + if (!s) + s = L""; + + return Callback->ReportUpdateOpeartion(op, s, isDir); + + COM_TRY_END +} + +STDMETHODIMP CArchiveUpdateCallback::ReportExtractResult(UInt32 indexType, UInt32 index, Int32 opRes) +{ + COM_TRY_BEGIN + + bool isEncrypted = false; + wchar_t temp[16]; + UString s2; + const wchar_t *s = NULL; + + if (indexType == NArchive::NEventIndexType::kOutArcIndex) + { + /* + UString name; + if (index != (UInt32)(Int32)-1) + { + const CUpdatePair2 &up = (*UpdatePairs)[index]; + if (up.ExistOnDisk()) + { + s2 = DirItems->GetLogPath(up.DirIndex); + s = s2; + } + } + */ + return E_FAIL; + } + + if (indexType == NArchive::NEventIndexType::kInArcIndex) + { + if (index != (UInt32)(Int32)-1) + { + if (ArcItems) + s = (*ArcItems)[index].Name; + else if (Arc) + { + RINOK(Arc->GetItemPath(index, s2)); + s = s2; + } + if (Archive) + { + RINOK(Archive_GetItemBoolProp(Archive, index, kpidEncrypted, isEncrypted)); + } + } + } + else if (indexType == NArchive::NEventIndexType::kBlockIndex) + { + temp[0] = '#'; + ConvertUInt32ToString(index, temp + 1); + s = temp; + } + + return Callback->ReportExtractResult(opRes, BoolToInt(isEncrypted), s); + COM_TRY_END } @@ -235,10 +695,10 @@ STDMETHODIMP CArchiveUpdateCallback::GetVolumeStream(UInt32 index, ISequentialOu FChar temp[16]; ConvertUInt32ToString(index + 1, temp); FString res = temp; - while (res.Length() < 2) - res = FString(FTEXT('0')) + res; + while (res.Len() < 2) + res.InsertAtFront(FTEXT('0')); FString fileName = VolName; - fileName += L'.'; + fileName += FTEXT('.'); fileName += res; fileName += VolExt; COutFileStream *streamSpec = new COutFileStream; @@ -263,3 +723,37 @@ STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword(BSTR *password) return Callback->CryptoGetTextPassword(password); COM_TRY_END } + +HRESULT CArchiveUpdateCallback::InFileStream_On_Error(UINT_PTR val, DWORD error) +{ + if (error == ERROR_LOCK_VIOLATION) + { + MT_LOCK + UInt32 index = (UInt32)val; + FOR_VECTOR(i, _openFiles_Indexes) + { + if (_openFiles_Indexes[i] == index) + { + RINOK(Callback->ReadingFileError(_openFiles_Paths[i], error)); + break; + } + } + } + return HRESULT_FROM_WIN32(error); +} + +void CArchiveUpdateCallback::InFileStream_On_Destroy(UINT_PTR val) +{ + MT_LOCK + UInt32 index = (UInt32)val; + FOR_VECTOR(i, _openFiles_Indexes) + { + if (_openFiles_Indexes[i] == index) + { + _openFiles_Indexes.Delete(i); + _openFiles_Paths.Delete(i); + return; + } + } + throw 20141125; +} diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/UpdateCallback.h b/3rdparty/lzma/CPP/7zip/UI/Common/UpdateCallback.h index 09f1c0d3705..4ff4a7ebe1d 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/UpdateCallback.h +++ b/3rdparty/lzma/CPP/7zip/UI/Common/UpdateCallback.h @@ -1,10 +1,11 @@ // UpdateCallback.h -#ifndef __UPDATECALLBACK_H -#define __UPDATECALLBACK_H +#ifndef __UPDATE_CALLBACK_H +#define __UPDATE_CALLBACK_H -#include "Common/MyCom.h" -#include "Common/MyString.h" +#include "../../../Common/MyCom.h" + +#include "../../Common/FileStreams.h" #include "../../IPassword.h" #include "../../ICoder.h" @@ -12,64 +13,135 @@ #include "../Common/UpdatePair.h" #include "../Common/UpdateProduce.h" +#include "OpenArchive.h" + #define INTERFACE_IUpdateCallbackUI(x) \ + virtual HRESULT WriteSfx(const wchar_t *name, UInt64 size) x; \ virtual HRESULT SetTotal(UInt64 size) x; \ virtual HRESULT SetCompleted(const UInt64 *completeValue) x; \ virtual HRESULT SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) x; \ virtual HRESULT CheckBreak() x; \ - virtual HRESULT Finilize() x; \ - virtual HRESULT SetNumFiles(UInt64 numFiles) x; \ - virtual HRESULT GetStream(const wchar_t *name, bool isAnti) x; \ - virtual HRESULT OpenFileError(const wchar_t *name, DWORD systemError) x; \ - virtual HRESULT SetOperationResult(Int32 operationResult) x; \ + /* virtual HRESULT Finalize() x; */ \ + virtual HRESULT SetNumItems(UInt64 numItems) x; \ + virtual HRESULT GetStream(const wchar_t *name, bool isDir, bool isAnti, UInt32 mode) x; \ + virtual HRESULT OpenFileError(const FString &path, DWORD systemError) x; \ + virtual HRESULT ReadingFileError(const FString &path, DWORD systemError) x; \ + virtual HRESULT SetOperationResult(Int32 opRes) x; \ + virtual HRESULT ReportExtractResult(Int32 opRes, Int32 isEncrypted, const wchar_t *name) x; \ + virtual HRESULT ReportUpdateOpeartion(UInt32 op, const wchar_t *name, bool isDir) x; \ + /* virtual HRESULT SetPassword(const UString &password) x; */ \ virtual HRESULT CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password) x; \ virtual HRESULT CryptoGetTextPassword(BSTR *password) x; \ - /* virtual HRESULT ShowDeleteFile(const wchar_t *name) x; */ \ - /* virtual HRESULT CloseProgress() { return S_OK; }; */ + virtual HRESULT ShowDeleteFile(const wchar_t *name, bool isDir) x; \ + /* virtual HRESULT CloseProgress() { return S_OK; } */ struct IUpdateCallbackUI { INTERFACE_IUpdateCallbackUI(=0) }; +struct CKeyKeyValPair +{ + UInt64 Key1; + UInt64 Key2; + unsigned Value; + + int Compare(const CKeyKeyValPair &a) const + { + if (Key1 < a.Key1) return -1; + if (Key1 > a.Key1) return 1; + return MyCompare(Key2, a.Key2); + } +}; + + class CArchiveUpdateCallback: public IArchiveUpdateCallback2, + public IArchiveUpdateCallbackFile, + public IArchiveExtractCallbackMessage, + public IArchiveGetRawProps, + public IArchiveGetRootProps, public ICryptoGetTextPassword2, public ICryptoGetTextPassword, public ICompressProgressInfo, + public IInFileStream_Callback, public CMyUnknownImp { + #if defined(_WIN32) && !defined(UNDER_CE) + bool _saclEnabled; + #endif + CRecordVector<CKeyKeyValPair> _map; + + UInt32 _hardIndex_From; + UInt32 _hardIndex_To; + public: - MY_UNKNOWN_IMP4( - IArchiveUpdateCallback2, - ICryptoGetTextPassword2, - ICryptoGetTextPassword, - ICompressProgressInfo) + MY_QUERYINTERFACE_BEGIN2(IArchiveUpdateCallback2) + MY_QUERYINTERFACE_ENTRY(IArchiveUpdateCallbackFile) + MY_QUERYINTERFACE_ENTRY(IArchiveExtractCallbackMessage) + MY_QUERYINTERFACE_ENTRY(IArchiveGetRawProps) + MY_QUERYINTERFACE_ENTRY(IArchiveGetRootProps) + MY_QUERYINTERFACE_ENTRY(ICryptoGetTextPassword2) + MY_QUERYINTERFACE_ENTRY(ICryptoGetTextPassword) + MY_QUERYINTERFACE_ENTRY(ICompressProgressInfo) + MY_QUERYINTERFACE_END + MY_ADDREF_RELEASE + STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize); INTERFACE_IArchiveUpdateCallback2(;) + INTERFACE_IArchiveUpdateCallbackFile(;) + INTERFACE_IArchiveExtractCallbackMessage(;) + INTERFACE_IArchiveGetRawProps(;) + INTERFACE_IArchiveGetRootProps(;) STDMETHOD(CryptoGetTextPassword2)(Int32 *passwordIsDefined, BSTR *password); STDMETHOD(CryptoGetTextPassword)(BSTR *password); -public: + CRecordVector<UInt32> _openFiles_Indexes; + FStringVector _openFiles_Paths; + + bool AreAllFilesClosed() const { return _openFiles_Indexes.IsEmpty(); } + virtual HRESULT InFileStream_On_Error(UINT_PTR val, DWORD error); + virtual void InFileStream_On_Destroy(UINT_PTR val); + CRecordVector<UInt64> VolumesSizes; FString VolName; FString VolExt; IUpdateCallbackUI *Callback; - bool ShareForWrite; - bool StdInMode; const CDirItems *DirItems; + const CDirItem *ParentDirItem; + + const CArc *Arc; + CMyComPtr<IInArchive> Archive; const CObjectVector<CArcItem> *ArcItems; const CRecordVector<CUpdatePair2> *UpdatePairs; const UStringVector *NewNames; - CMyComPtr<IInArchive> Archive; + + bool ShareForWrite; + bool StdInMode; + bool KeepOriginalItemNames; + bool StoreNtSecurity; + bool StoreHardLinks; + bool StoreSymLinks; + + Byte *ProcessedItemsStatuses; + CArchiveUpdateCallback(); + + bool IsDir(const CUpdatePair2 &up) const + { + if (up.DirIndex >= 0) + return DirItems->Items[up.DirIndex].IsDir(); + else if (up.ArcIndex >= 0) + return (*ArcItems)[up.ArcIndex].IsDir; + return false; + } }; #endif diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/UpdatePair.cpp b/3rdparty/lzma/CPP/7zip/UI/Common/UpdatePair.cpp index a43a9e77055..e21151974be 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/UpdatePair.cpp +++ b/3rdparty/lzma/CPP/7zip/UI/Common/UpdatePair.cpp @@ -4,10 +4,9 @@ #include <time.h> -#include "Common/Defs.h" -#include "Common/Wildcard.h" +#include "../../../Common/Wildcard.h" -#include "Windows/Time.h" +#include "../../../Windows/TimeUtils.h" #include "SortUtils.h" #include "UpdatePair.h" @@ -17,7 +16,7 @@ using namespace NTime; static int MyCompareTime(NFileTimeType::EEnum fileTimeType, const FILETIME &time1, const FILETIME &time2) { - switch(fileTimeType) + switch (fileTimeType) { case NFileTimeType::kWindows: return ::CompareFileTime(&time1, &time2); @@ -39,24 +38,38 @@ static int MyCompareTime(NFileTimeType::EEnum fileTimeType, const FILETIME &time throw 4191618; } -static const wchar_t *kDuplicateFileNameMessage = L"Duplicate filename:"; -static const wchar_t *kNotCensoredCollisionMessaged = L"Internal file name collision (file on disk, file in archive):"; +static const char *k_Duplicate_inArc_Message = "Duplicate filename in archive:"; +static const char *k_Duplicate_inDir_Message = "Duplicate filename on disk:"; +static const char *k_NotCensoredCollision_Message = "Internal file name collision (file on disk, file in archive):"; -static void ThrowError(const UString &message, const UString &s1, const UString &s2) +static void ThrowError(const char *message, const UString &s1, const UString &s2) { - UString m = message; - m += L'\n'; - m += s1; - m += L'\n'; - m += s2; + UString m; + m.SetFromAscii(message); + m.Add_LF(); m += s1; + m.Add_LF(); m += s2; throw m; } -static void TestDuplicateString(const UStringVector &strings, const CIntVector &indices) +static int CompareArcItemsBase(const CArcItem &ai1, const CArcItem &ai2) { - for(int i = 0; i + 1 < indices.Size(); i++) - if (CompareFileNames(strings[indices[i]], strings[indices[i + 1]]) == 0) - ThrowError(kDuplicateFileNameMessage, strings[indices[i]], strings[indices[i + 1]]); + int res = CompareFileNames(ai1.Name, ai2.Name); + if (res != 0) + return res; + if (ai1.IsDir != ai2.IsDir) + return ai1.IsDir ? -1 : 1; + return 0; +} + +static int CompareArcItems(const unsigned *p1, const unsigned *p2, void *param) +{ + unsigned i1 = *p1; + unsigned i2 = *p2; + const CObjectVector<CArcItem> &arcItems = *(const CObjectVector<CArcItem> *)param; + int res = CompareArcItemsBase(arcItems[i1], arcItems[i2]); + if (res != 0) + return res; + return MyCompare(i1, i2); } void GetUpdatePairInfoList( @@ -65,48 +78,104 @@ void GetUpdatePairInfoList( NFileTimeType::EEnum fileTimeType, CRecordVector<CUpdatePair> &updatePairs) { - CIntVector dirIndices, arcIndices; - - int numDirItems = dirItems.Items.Size(); - int numArcItems = arcItems.Size(); + CUIntVector dirIndices, arcIndices; + unsigned numDirItems = dirItems.Items.Size(); + unsigned numArcItems = arcItems.Size(); + CIntArr duplicatedArcItem(numArcItems); { - UStringVector arcNames; - arcNames.Reserve(numArcItems); - for (int i = 0; i < numArcItems; i++) - arcNames.Add(arcItems[i].Name); - SortFileNames(arcNames, arcIndices); - TestDuplicateString(arcNames, arcIndices); + int *vals = &duplicatedArcItem[0]; + for (unsigned i = 0; i < numArcItems; i++) + vals[i] = 0; + } + + { + arcIndices.ClearAndSetSize(numArcItems); + if (numArcItems != 0) + { + unsigned *vals = &arcIndices[0]; + for (unsigned i = 0; i < numArcItems; i++) + vals[i] = i; + } + arcIndices.Sort(CompareArcItems, (void *)&arcItems); + for (unsigned i = 0; i + 1 < numArcItems; i++) + if (CompareArcItemsBase( + arcItems[arcIndices[i]], + arcItems[arcIndices[i + 1]]) == 0) + { + duplicatedArcItem[i] = 1; + duplicatedArcItem[i + 1] = -1; + } } UStringVector dirNames; { - dirNames.Reserve(numDirItems); - for (int i = 0; i < numDirItems; i++) - dirNames.Add(dirItems.GetLogPath(i)); + dirNames.ClearAndReserve(numDirItems); + unsigned i; + for (i = 0; i < numDirItems; i++) + dirNames.AddInReserved(dirItems.GetLogPath(i)); SortFileNames(dirNames, dirIndices); - TestDuplicateString(dirNames, dirIndices); + for (i = 0; i + 1 < numDirItems; i++) + { + const UString &s1 = dirNames[dirIndices[i]]; + const UString &s2 = dirNames[dirIndices[i + 1]]; + if (CompareFileNames(s1, s2) == 0) + ThrowError(k_Duplicate_inDir_Message, s1, s2); + } } - int dirIndex = 0, arcIndex = 0; - while (dirIndex < numDirItems && arcIndex < numArcItems) + unsigned dirIndex = 0; + unsigned arcIndex = 0; + + int prevHostFile = -1; + const UString *prevHostName = NULL; + + while (dirIndex < numDirItems || arcIndex < numArcItems) { CUpdatePair pair; - int dirIndex2 = dirIndices[dirIndex]; - int arcIndex2 = arcIndices[arcIndex]; - const CDirItem &di = dirItems.Items[dirIndex2]; - const CArcItem &ai = arcItems[arcIndex2]; - int compareResult = CompareFileNames(dirNames[dirIndex2], ai.Name); + + int dirIndex2 = -1; + int arcIndex2 = -1; + const CDirItem *di = NULL; + const CArcItem *ai = NULL; + + int compareResult = -1; + const UString *name = NULL; + + if (dirIndex < numDirItems) + { + dirIndex2 = dirIndices[dirIndex]; + di = &dirItems.Items[dirIndex2]; + } + + if (arcIndex < numArcItems) + { + arcIndex2 = arcIndices[arcIndex]; + ai = &arcItems[arcIndex2]; + compareResult = 1; + if (dirIndex < numDirItems) + { + compareResult = CompareFileNames(dirNames[dirIndex2], ai->Name); + if (compareResult == 0) + { + if (di->IsDir() != ai->IsDir) + compareResult = (ai->IsDir ? 1 : -1); + } + } + } + if (compareResult < 0) { + name = &dirNames[dirIndex2]; pair.State = NUpdateArchive::NPairState::kOnlyOnDisk; pair.DirIndex = dirIndex2; dirIndex++; } else if (compareResult > 0) { - pair.State = ai.Censored ? + name = &ai->Name; + pair.State = ai->Censored ? NUpdateArchive::NPairState::kOnlyInArchive: NUpdateArchive::NPairState::kNotMasked; pair.ArcIndex = arcIndex2; @@ -114,43 +183,50 @@ void GetUpdatePairInfoList( } else { - if (!ai.Censored) - ThrowError(kNotCensoredCollisionMessaged, dirNames[dirIndex2], ai.Name); + int dupl = duplicatedArcItem[arcIndex]; + if (dupl != 0) + ThrowError(k_Duplicate_inArc_Message, ai->Name, arcItems[arcIndices[arcIndex + dupl]].Name); + + name = &dirNames[dirIndex2]; + if (!ai->Censored) + ThrowError(k_NotCensoredCollision_Message, *name, ai->Name); + pair.DirIndex = dirIndex2; pair.ArcIndex = arcIndex2; - switch (ai.MTimeDefined ? MyCompareTime( - ai.TimeType != - 1 ? (NFileTimeType::EEnum)ai.TimeType : fileTimeType, - di.MTime, ai.MTime): 0) + + switch (ai->MTimeDefined ? MyCompareTime( + ai->TimeType != - 1 ? (NFileTimeType::EEnum)ai->TimeType : fileTimeType, + di->MTime, ai->MTime): 0) { case -1: pair.State = NUpdateArchive::NPairState::kNewInArchive; break; - case 1: pair.State = NUpdateArchive::NPairState::kOldInArchive; break; + case 1: pair.State = NUpdateArchive::NPairState::kOldInArchive; break; default: - pair.State = (ai.SizeDefined && di.Size == ai.Size) ? + pair.State = (ai->SizeDefined && di->Size == ai->Size) ? NUpdateArchive::NPairState::kSameFiles : NUpdateArchive::NPairState::kUnknowNewerFiles; } + dirIndex++; arcIndex++; } - updatePairs.Add(pair); - } - - for (; dirIndex < numDirItems; dirIndex++) - { - CUpdatePair pair; - pair.State = NUpdateArchive::NPairState::kOnlyOnDisk; - pair.DirIndex = dirIndices[dirIndex]; - updatePairs.Add(pair); - } - - for (; arcIndex < numArcItems; arcIndex++) - { - CUpdatePair pair; - int arcIndex2 = arcIndices[arcIndex]; - pair.State = arcItems[arcIndex2].Censored ? - NUpdateArchive::NPairState::kOnlyInArchive: - NUpdateArchive::NPairState::kNotMasked; - pair.ArcIndex = arcIndex2; + + if ((di && di->IsAltStream) || + (ai && ai->IsAltStream)) + { + if (prevHostName) + { + unsigned hostLen = prevHostName->Len(); + if (name->Len() > hostLen) + if ((*name)[hostLen] == ':' && CompareFileNames(*prevHostName, name->Left(hostLen)) == 0) + pair.HostIndex = prevHostFile; + } + } + else + { + prevHostFile = updatePairs.Size(); + prevHostName = name; + } + updatePairs.Add(pair); } diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/UpdatePair.h b/3rdparty/lzma/CPP/7zip/UI/Common/UpdatePair.h index 3a332649c91..296d3b09745 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/UpdatePair.h +++ b/3rdparty/lzma/CPP/7zip/UI/Common/UpdatePair.h @@ -13,7 +13,9 @@ struct CUpdatePair NUpdateArchive::NPairState::EEnum State; int ArcIndex; int DirIndex; - CUpdatePair(): ArcIndex(-1), DirIndex(-1) {} + int HostIndex; // >= 0 for alt streams only, contains index of host pair + + CUpdatePair(): ArcIndex(-1), DirIndex(-1), HostIndex(-1) {} }; void GetUpdatePairInfoList( diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/UpdateProduce.cpp b/3rdparty/lzma/CPP/7zip/UI/Common/UpdateProduce.cpp index c21db3b2a63..e6eabcf148e 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/UpdateProduce.cpp +++ b/3rdparty/lzma/CPP/7zip/UI/Common/UpdateProduce.cpp @@ -14,32 +14,41 @@ void UpdateProduce( CRecordVector<CUpdatePair2> &operationChain, IUpdateProduceCallback *callback) { - for (int i = 0; i < updatePairs.Size(); i++) + FOR_VECTOR (i, updatePairs) { const CUpdatePair &pair = updatePairs[i]; CUpdatePair2 up2; - up2.IsAnti = false; up2.DirIndex = pair.DirIndex; up2.ArcIndex = pair.ArcIndex; up2.NewData = up2.NewProps = true; + up2.UseArcProps = false; - switch(actionSet.StateActions[pair.State]) + switch (actionSet.StateActions[(unsigned)pair.State]) { case NPairAction::kIgnore: - /* - if (pair.State != NPairState::kOnlyOnDisk) - IgnoreArchiveItem(m_ArchiveItems[pair.ArcIndex]); - // cout << "deleting"; - */ - if (callback) + if (pair.ArcIndex >= 0 && callback) callback->ShowDeleteFile(pair.ArcIndex); continue; case NPairAction::kCopy: if (pair.State == NPairState::kOnlyOnDisk) throw kUpdateActionSetCollision; + if (pair.State == NPairState::kOnlyInArchive) + { + if (pair.HostIndex >= 0) + { + /* + ignore alt stream if + 1) no such alt stream in Disk + 2) there is Host file in disk + */ + if (updatePairs[pair.HostIndex].DirIndex >= 0) + continue; + } + } up2.NewData = up2.NewProps = false; + up2.UseArcProps = true; break; case NPairAction::kCompress: @@ -50,9 +59,12 @@ void UpdateProduce( case NPairAction::kCompressAsAnti: up2.IsAnti = true; + up2.UseArcProps = (pair.ArcIndex >= 0); break; } + operationChain.Add(up2); } + operationChain.ReserveDown(); } diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/UpdateProduce.h b/3rdparty/lzma/CPP/7zip/UI/Common/UpdateProduce.h index e18648cd9bd..64c58cc5772 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/UpdateProduce.h +++ b/3rdparty/lzma/CPP/7zip/UI/Common/UpdateProduce.h @@ -9,21 +9,41 @@ struct CUpdatePair2 { bool NewData; bool NewProps; - bool IsAnti; + bool UseArcProps; // if (UseArcProps && NewProps), we want to change only some properties. + bool IsAnti; // if (!IsAnti) we use other ways to detect Anti status int DirIndex; int ArcIndex; int NewNameIndex; + bool IsMainRenameItem; + + void SetAs_NoChangeArcItem(int arcIndex) + { + NewData = NewProps = false; + UseArcProps = true; + IsAnti = false; + ArcIndex = arcIndex; + } + bool ExistOnDisk() const { return DirIndex != -1; } bool ExistInArchive() const { return ArcIndex != -1; } - CUpdatePair2(): IsAnti(false), DirIndex(-1), ArcIndex(-1), NewNameIndex(-1) {} + CUpdatePair2(): + NewData(false), + NewProps(false), + UseArcProps(false), + IsAnti(false), + DirIndex(-1), + ArcIndex(-1), + NewNameIndex(-1), + IsMainRenameItem(false) + {} }; struct IUpdateProduceCallback { - virtual HRESULT ShowDeleteFile(int arcIndex) = 0; + virtual HRESULT ShowDeleteFile(unsigned arcIndex) = 0; }; void UpdateProduce( diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/WorkDir.cpp b/3rdparty/lzma/CPP/7zip/UI/Common/WorkDir.cpp index 1ecb5b54066..284eaa16ac3 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/WorkDir.cpp +++ b/3rdparty/lzma/CPP/7zip/UI/Common/WorkDir.cpp @@ -2,21 +2,22 @@ #include "StdAfx.h" -#include "Common/StringConvert.h" -#include "Common/Wildcard.h" +#include "../../../Common/StringConvert.h" +#include "../../../Common/Wildcard.h" -#include "Windows/FileName.h" +#include "../../../Windows/FileName.h" #include "WorkDir.h" using namespace NWindows; using namespace NFile; -using namespace NDirectory; +using namespace NDir; FString GetWorkDir(const NWorkDir::CInfo &workDirInfo, const FString &path, FString &fileName) { NWorkDir::NMode::EEnum mode = workDirInfo.Mode; - #ifndef UNDER_CE + + #if defined(_WIN32) && !defined(UNDER_CE) if (workDirInfo.ForRemovableOnly) { mode = NWorkDir::NMode::kCurrent; @@ -36,13 +37,15 @@ FString GetWorkDir(const NWorkDir::CInfo &workDirInfo, const FString &path, FStr */ } #endif - int pos = path.ReverseFind(FCHAR_PATH_SEPARATOR) + 1; - fileName = path.Mid(pos); + + int pos = path.ReverseFind_PathSepar() + 1; + fileName = path.Ptr(pos); + switch (mode) { case NWorkDir::NMode::kCurrent: { - return path.Left(pos);; + return path.Left(pos); } case NWorkDir::NMode::kSpecified: { @@ -66,7 +69,7 @@ HRESULT CWorkDirTempFile::CreateTempFile(const FString &originalPath) workDirInfo.Load(); FString namePart; FString workDir = GetWorkDir(workDirInfo, originalPath, namePart); - CreateComplexDirectory(workDir); + CreateComplexDir(workDir); CTempFile tempFile; _outStreamSpec = new COutFileStream; OutStream = _outStreamSpec; diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/WorkDir.h b/3rdparty/lzma/CPP/7zip/UI/Common/WorkDir.h index 79ea2b9d856..75850a92b1c 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/WorkDir.h +++ b/3rdparty/lzma/CPP/7zip/UI/Common/WorkDir.h @@ -3,18 +3,18 @@ #ifndef __WORK_DIR_H #define __WORK_DIR_H -#include "Windows/FileDir.h" - -#include "ZipRegistry.h" +#include "../../../Windows/FileDir.h" #include "../../Common/FileStreams.h" +#include "ZipRegistry.h" + FString GetWorkDir(const NWorkDir::CInfo &workDirInfo, const FString &path, FString &fileName); class CWorkDirTempFile { FString _originalPath; - NWindows::NFile::NDirectory::CTempFile _tempFile; + NWindows::NFile::NDir::CTempFile _tempFile; COutFileStream *_outStreamSpec; public: CMyComPtr<IOutStream> OutStream; diff --git a/3rdparty/lzma/CPP/7zip/UI/Common/ZipRegistry.h b/3rdparty/lzma/CPP/7zip/UI/Common/ZipRegistry.h index 6d8b7c1337d..935f97bbaa4 100644 --- a/3rdparty/lzma/CPP/7zip/UI/Common/ZipRegistry.h +++ b/3rdparty/lzma/CPP/7zip/UI/Common/ZipRegistry.h @@ -3,8 +3,8 @@ #ifndef __ZIP_REGISTRY_H #define __ZIP_REGISTRY_H -#include "Common/MyString.h" -#include "Common/Types.h" +#include "../../../Common/MyTypes.h" +#include "../../../Common/MyString.h" #include "ExtractMode.h" @@ -14,12 +14,21 @@ namespace NExtract { NPathMode::EEnum PathMode; NOverwriteMode::EEnum OverwriteMode; - bool ShowPassword; + bool PathMode_Force; + bool OverwriteMode_Force; + + CBoolPair SplitDest; + CBoolPair ElimDup; + // CBoolPair AltStreams; + CBoolPair NtSecurity; + CBoolPair ShowPassword; + UStringVector Paths; void Save() const; void Load(); }; + void Save_ShowPassword(bool showPassword); bool Read_ShowPassword(); } @@ -59,6 +68,11 @@ namespace NCompression CObjectVector<CFormatOptions> Formats; + CBoolPair NtSecurity; + CBoolPair AltStreams; + CBoolPair HardLinks; + CBoolPair SymLinks; + void Save() const; void Load(); }; @@ -97,7 +111,11 @@ namespace NWorkDir struct CContextMenuInfo { - bool Cascaded; + CBoolPair Cascaded; + CBoolPair MenuIcons; + CBoolPair ElimDup; + + bool Flags_Def; UInt32 Flags; void Save() const; |