| // ArchiveCommandLine.cpp |
| |
| #include "StdAfx.h" |
| |
| #ifdef _WIN32 |
| #ifndef UNDER_CE |
| #include <io.h> |
| #endif |
| #endif |
| #include <stdio.h> |
| |
| #include "Common/ListFileUtils.h" |
| #include "Common/StringConvert.h" |
| #include "Common/StringToInt.h" |
| |
| #include "Windows/FileDir.h" |
| #include "Windows/FileName.h" |
| #ifdef _WIN32 |
| #include "Windows/FileMapping.h" |
| #include "Windows/Synchronization.h" |
| #else |
| #include "myPrivate.h" |
| #endif |
| |
| #include "ArchiveCommandLine.h" |
| #include "EnumDirItems.h" |
| #include "SortUtils.h" |
| #include "Update.h" |
| #include "UpdateAction.h" |
| |
| extern bool g_CaseSensitive; |
| |
| #ifdef UNDER_CE |
| |
| #define MY_IS_TERMINAL(x) false; |
| |
| #else |
| |
| #if _MSC_VER >= 1400 |
| #define MY_isatty_fileno(x) _isatty(_fileno(x)) |
| #else |
| #define MY_isatty_fileno(x) isatty(fileno(x)) |
| #endif |
| |
| #define MY_IS_TERMINAL(x) (MY_isatty_fileno(x) != 0); |
| |
| #endif |
| |
| using namespace NCommandLineParser; |
| using namespace NWindows; |
| using namespace NFile; |
| |
| namespace NKey { |
| enum Enum |
| { |
| kHelp1 = 0, |
| kHelp2, |
| kHelp3, |
| kDisableHeaders, |
| kDisablePercents, |
| kArchiveType, |
| kYes, |
| #ifndef _NO_CRYPTO |
| kPassword, |
| #endif |
| kProperty, |
| kOutputDir, |
| kWorkingDir, |
| kInclude, |
| kExclude, |
| kArInclude, |
| kArExclude, |
| kNoArName, |
| kUpdate, |
| kVolume, |
| kRecursed, |
| kSfx, |
| kStdIn, |
| kStdOut, |
| kOverwrite, |
| kEmail, |
| kShowDialog, |
| kLargePages, |
| kUseLStat, |
| kTechMode, |
| kCaseSensitive, |
| kCalcCrc |
| }; |
| |
| } |
| |
| |
| static const wchar_t kRecursedIDChar = 'R'; |
| static const wchar_t *kRecursedPostCharSet = L"0-"; |
| |
| namespace NRecursedPostCharIndex { |
| enum EEnum |
| { |
| kWildCardRecursionOnly = 0, |
| kNoRecursion = 1 |
| }; |
| } |
| |
| static const char kImmediateNameID = '!'; |
| static const char kMapNameID = '#'; |
| 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"; |
| |
| NExtract::NOverwriteMode::EEnum k_OverwriteModes[] = |
| { |
| NExtract::NOverwriteMode::kWithoutPrompt, |
| NExtract::NOverwriteMode::kSkipExisting, |
| NExtract::NOverwriteMode::kAutoRename, |
| NExtract::NOverwriteMode::kAutoRenameExisting |
| }; |
| |
| 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"L", NSwitchType::kSimple, false }, |
| { L"SLT", NSwitchType::kSimple, false }, |
| { L"SSC", NSwitchType::kPostChar, false, 0, 0, L"-" }, |
| { L"SCRC", NSwitchType::kSimple, false } |
| }; |
| |
| 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 } |
| }; |
| |
| static const int kNumCommandForms = sizeof(g_CommandForms) / sizeof(g_CommandForms[0]); |
| |
| static const wchar_t *kUniversalWildcard = L"*"; |
| static const int kMinNonSwitchWords = 1; |
| static const int kCommandIndex = 0; |
| |
| // --------------------------- |
| // exception messages |
| |
| 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 *kEmptyFilePath = "Empty file path"; |
| |
| static void ThrowException(const char *errorMessage) |
| { |
| throw CArchiveCommandLineException(errorMessage); |
| } |
| |
| static void ThrowUserErrorException() |
| { |
| ThrowException(kUserErrorMessage); |
| } |
| |
| // --------------------------- |
| |
| bool CArchiveCommand::IsFromExtractGroup() const |
| { |
| switch(CommandType) |
| { |
| case NCommandType::kTest: |
| case NCommandType::kExtract: |
| case NCommandType::kFullExtract: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| NExtract::NPathMode::EEnum CArchiveCommand::GetPathMode() const |
| { |
| switch(CommandType) |
| { |
| case NCommandType::kTest: |
| case NCommandType::kFullExtract: |
| return NExtract::NPathMode::kFullPathnames; |
| default: |
| return NExtract::NPathMode::kNoPathnames; |
| } |
| } |
| |
| bool CArchiveCommand::IsFromUpdateGroup() const |
| { |
| return (CommandType == NCommandType::kAdd || |
| CommandType == NCommandType::kUpdate || |
| CommandType == NCommandType::kDelete); |
| } |
| |
| static NRecursedType::EEnum GetRecursedTypeFromIndex(int index) |
| { |
| switch (index) |
| { |
| case NRecursedPostCharIndex::kWildCardRecursionOnly: |
| return NRecursedType::kWildCardOnlyRecursed; |
| case NRecursedPostCharIndex::kNoRecursion: |
| return NRecursedType::kNonRecursed; |
| default: |
| return NRecursedType::kRecursed; |
| } |
| } |
| |
| static bool ParseArchiveCommand(const UString &commandString, CArchiveCommand &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; |
| } |
| |
| // ------------------------------------------------------------------ |
| // filenames functions |
| |
| static void AddNameToCensor(NWildcard::CCensor &wildcardCensor, |
| const UString &name, bool include, NRecursedType::EEnum type) |
| { |
| bool recursed = false; |
| |
| switch (type) |
| { |
| case NRecursedType::kWildCardOnlyRecursed: |
| recursed = DoesNameContainWildCard(name); |
| break; |
| case NRecursedType::kRecursed: |
| recursed = true; |
| break; |
| } |
| wildcardCensor.AddItem(include, name, recursed); |
| } |
| |
| static void AddToCensorFromListFile(NWildcard::CCensor &wildcardCensor, |
| LPCWSTR fileName, bool include, NRecursedType::EEnum type, UINT codePage) |
| { |
| UStringVector names; |
| if (!NFind::DoesFileExist(fileName)) |
| throw kCannotFindListFile; |
| if (!ReadNamesFromListFile(fileName, names, codePage)) |
| throw kIncorrectListFile; |
| for (int i = 0; i < names.Size(); i++) |
| AddNameToCensor(wildcardCensor, names[i], include, type); |
| } |
| |
| static void AddToCensorFromNonSwitchesStrings( |
| int startIndex, |
| NWildcard::CCensor &wildcardCensor, |
| const UStringVector &nonSwitchStrings, NRecursedType::EEnum type, |
| bool thereAreSwitchIncludes, UINT codePage) |
| { |
| if (nonSwitchStrings.Size() == startIndex && (!thereAreSwitchIncludes)) |
| AddNameToCensor(wildcardCensor, kUniversalWildcard, true, type); |
| for (int i = startIndex; i < nonSwitchStrings.Size(); i++) |
| { |
| const UString &s = nonSwitchStrings[i]; |
| if (s.IsEmpty()) |
| throw 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 |
| { |
| 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++) |
| { |
| wchar_t c = curData[i]; |
| if (c == L'\0') |
| { |
| AddNameToCensor(wildcardCensor, name, include, commonRecursedType); |
| name.Empty(); |
| } |
| else |
| name += c; |
| } |
| if (!name.IsEmpty()) |
| ThrowException("data error"); |
| } |
| catch(...) |
| { |
| UnmapViewOfFile(data); |
| throw; |
| } |
| UnmapViewOfFile(data); |
| } |
| |
| { |
| NSynchronization::CManualResetEvent event; |
| if (event.Open(EVENT_MODIFY_STATE, false, GetSystemString(eventName)) == S_OK) |
| event.Set(); |
| } |
| } |
| #endif |
| |
| static void AddSwitchWildCardsToCensor(NWildcard::CCensor &wildcardCensor, |
| const UStringVector &strings, bool include, |
| NRecursedType::EEnum commonRecursedType, UINT codePage) |
| { |
| for (int 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) |
| { |
| pos++; |
| int index = UString(kRecursedPostCharSet).Find(name[pos]); |
| recursedType = GetRecursedTypeFromIndex(index); |
| if (index >= 0) |
| pos++; |
| } |
| else |
| recursedType = commonRecursedType; |
| if (name.Length() < pos + kSomeCludeAfterRecursedPostStringMinSize) |
| ThrowUserErrorException(); |
| UString tail = name.Mid(pos + 1); |
| if (name[pos] == kImmediateNameID) |
| AddNameToCensor(wildcardCensor, tail, include, recursedType); |
| else if (name[pos] == kFileListID) |
| AddToCensorFromListFile(wildcardCensor, tail, include, recursedType, codePage); |
| #ifdef _WIN32 |
| else if (name[pos] == kMapNameID) |
| ParseMapWithPaths(wildcardCensor, tail, include, recursedType); |
| #endif |
| else |
| ThrowUserErrorException(); |
| } |
| } |
| |
| #ifdef _WIN32 |
| |
| // This code converts all short file names to long file names. |
| |
| static void ConvertToLongName(const UString &prefix, UString &name) |
| { |
| if (name.IsEmpty() || DoesNameContainWildCard(name)) |
| return; |
| NFind::CFileInfoW fi; |
| if (fi.Find(prefix + name)) |
| name = fi.Name; |
| } |
| |
| static void ConvertToLongNames(const UString &prefix, CObjectVector<NWildcard::CItem> &items) |
| { |
| for (int i = 0; i < items.Size(); i++) |
| { |
| NWildcard::CItem &item = items[i]; |
| if (item.Recursive || item.PathParts.Size() != 1) |
| continue; |
| ConvertToLongName(prefix, item.PathParts.Front()); |
| } |
| } |
| |
| static void ConvertToLongNames(const UString &prefix, NWildcard::CCensorNode &node) |
| { |
| ConvertToLongNames(prefix, node.IncludeItems); |
| ConvertToLongNames(prefix, node.ExcludeItems); |
| int i; |
| for (i = 0; i < node.SubNodes.Size(); i++) |
| ConvertToLongName(prefix, node.SubNodes[i].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();) |
| { |
| const NWildcard::CCensorNode &nextNode2 = node.SubNodes[j]; |
| if (nextNode1.Name.CompareNoCase(nextNode2.Name) == 0) |
| { |
| nextNode1.IncludeItems += nextNode2.IncludeItems; |
| nextNode1.ExcludeItems += nextNode2.ExcludeItems; |
| node.SubNodes.Delete(j); |
| } |
| else |
| j++; |
| } |
| } |
| for (i = 0; i < node.SubNodes.Size(); i++) |
| { |
| NWildcard::CCensorNode &nextNode = node.SubNodes[i]; |
| ConvertToLongNames(prefix + nextNode.Name + wchar_t(NFile::NName::kDirDelimiter), nextNode); |
| } |
| } |
| |
| static void ConvertToLongNames(NWildcard::CCensor &censor) |
| { |
| for (int i = 0; i < censor.Pairs.Size(); i++) |
| { |
| NWildcard::CPair &pair = censor.Pairs[i]; |
| ConvertToLongNames(pair.Prefix, pair.Head); |
| } |
| } |
| |
| #endif |
| |
| static NUpdateArchive::NPairAction::EEnum GetUpdatePairActionType(int i) |
| { |
| switch(i) |
| { |
| case NUpdateArchive::NPairAction::kIgnore: return NUpdateArchive::NPairAction::kIgnore; |
| case NUpdateArchive::NPairAction::kCopy: return NUpdateArchive::NPairAction::kCopy; |
| case NUpdateArchive::NPairAction::kCompress: return NUpdateArchive::NPairAction::kCompress; |
| case NUpdateArchive::NPairAction::kCompressAsAnti: return NUpdateArchive::NPairAction::kCompressAsAnti; |
| } |
| 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 |
| |
| const wchar_t *kUpdateIgnoreItselfPostStringID = L"-"; |
| const wchar_t kUpdateNewArchivePostCharID = '!'; |
| |
| |
| static bool ParseUpdateCommandString2(const UString &command, |
| NUpdateArchive::CActionSet &actionSet, UString &postString) |
| { |
| for (int i = 0; i < command.Length();) |
| { |
| wchar_t c = MyCharUpper(command[i]); |
| int statePos = kUpdatePairStateIDSet.Find(c); |
| if (statePos < 0) |
| { |
| postString = command.Mid(i); |
| return true; |
| } |
| i++; |
| if (i >= command.Length()) |
| return false; |
| int actionPos = kUpdatePairActionIDSet.Find(::MyCharUpper(command[i])); |
| if (actionPos < 0) |
| return false; |
| actionSet.StateActions[statePos] = GetUpdatePairActionType(actionPos); |
| if (kUpdatePairStateNotSupportedActions[statePos] == actionPos) |
| return false; |
| i++; |
| } |
| postString.Empty(); |
| return true; |
| } |
| |
| static void ParseUpdateCommandString(CUpdateOptions &options, |
| const UStringVector &updatePostStrings, |
| const NUpdateArchive::CActionSet &defaultActionSet) |
| { |
| for (int i = 0; i < updatePostStrings.Size(); i++) |
| { |
| const UString &updateString = updatePostStrings[i]; |
| if (updateString.CompareNoCase(kUpdateIgnoreItselfPostStringID) == 0) |
| { |
| if (options.UpdateArchiveItself) |
| { |
| options.UpdateArchiveItself = false; |
| options.Commands.Delete(0); |
| } |
| } |
| else |
| { |
| NUpdateArchive::CActionSet actionSet = defaultActionSet; |
| |
| UString postString; |
| if (!ParseUpdateCommandString2(updateString, actionSet, postString)) |
| ThrowUserErrorException(); |
| if (postString.IsEmpty()) |
| { |
| if (options.UpdateArchiveItself) |
| options.Commands[0].ActionSet = actionSet; |
| } |
| else |
| { |
| if (MyCharUpper(postString[0]) != kUpdateNewArchivePostCharID) |
| ThrowUserErrorException(); |
| CUpdateArchiveCommand uc; |
| UString archivePath = postString.Mid(1); |
| if (archivePath.IsEmpty()) |
| ThrowUserErrorException(); |
| uc.UserArchivePath = archivePath; |
| uc.ActionSet = actionSet; |
| options.Commands.Add(uc); |
| } |
| } |
| } |
| } |
| |
| 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; |
| } |
| |
| static void SetAddCommandOptions( |
| NCommandType::EEnum commandType, |
| const CParser &parser, |
| CUpdateOptions &options) |
| { |
| NUpdateArchive::CActionSet defaultActionSet; |
| switch(commandType) |
| { |
| case NCommandType::kAdd: |
| defaultActionSet = NUpdateArchive::kAddActionSet; |
| break; |
| case NCommandType::kDelete: |
| defaultActionSet = NUpdateArchive::kDeleteActionSet; |
| break; |
| default: |
| defaultActionSet = NUpdateArchive::kUpdateActionSet; |
| } |
| |
| options.UpdateArchiveItself = true; |
| |
| options.Commands.Clear(); |
| CUpdateArchiveCommand updateMainCommand; |
| updateMainCommand.ActionSet = defaultActionSet; |
| options.Commands.Add(updateMainCommand); |
| if (parser[NKey::kUpdate].ThereIs) |
| ParseUpdateCommandString(options, parser[NKey::kUpdate].PostStrings, |
| defaultActionSet); |
| if (parser[NKey::kWorkingDir].ThereIs) |
| { |
| const UString &postString = parser[NKey::kWorkingDir].PostStrings[0]; |
| if (postString.IsEmpty()) |
| NDirectory::MyGetTempPath(options.WorkingDir); |
| else |
| options.WorkingDir = postString; |
| } |
| options.SfxMode = parser[NKey::kSfx].ThereIs; |
| if (options.SfxMode) |
| options.SfxModule = parser[NKey::kSfx].PostStrings[0]; |
| |
| if (parser[NKey::kVolume].ThereIs) |
| { |
| const UStringVector &sv = parser[NKey::kVolume].PostStrings; |
| for (int i = 0; i < sv.Size(); i++) |
| { |
| UInt64 size; |
| if (!ParseComplexSize(sv[i], size)) |
| ThrowException("Incorrect volume size"); |
| options.VolumesSizes.Add(size); |
| } |
| } |
| } |
| |
| static void SetMethodOptions(const CParser &parser, CObjectVector<CProperty> &properties) |
| { |
| if (parser[NKey::kProperty].ThereIs) |
| { |
| // options.MethodMode.Properties.Clear(); |
| for (int i = 0; i < parser[NKey::kProperty].PostStrings.Size(); i++) |
| { |
| CProperty property; |
| const UString &postString = parser[NKey::kProperty].PostStrings[i]; |
| int index = postString.Find(L'='); |
| if (index < 0) |
| property.Name = postString; |
| else |
| { |
| property.Name = postString.Left(index); |
| property.Value = postString.Mid(index + 1); |
| } |
| properties.Add(property); |
| } |
| } |
| } |
| |
| CArchiveCommandLineParser::CArchiveCommandLineParser(): |
| parser(sizeof(kSwitchForms) / sizeof(kSwitchForms[0])) {} |
| |
| void CArchiveCommandLineParser::Parse1(const UStringVector &commandStrings, |
| CArchiveCommandLineOptions &options) |
| { |
| try |
| { |
| parser.ParseStrings(kSwitchForms, commandStrings); |
| } |
| catch(...) |
| { |
| ThrowUserErrorException(); |
| } |
| |
| options.IsInTerminal = MY_IS_TERMINAL(stdin); |
| options.IsStdOutTerminal = MY_IS_TERMINAL(stdout); |
| options.IsStdErrTerminal = MY_IS_TERMINAL(stderr); |
| 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; |
| |
| #ifdef _7ZIP_LARGE_PAGES |
| options.LargePages = false; |
| if (parser[NKey::kLargePages].ThereIs) |
| { |
| const UString &postString = parser[NKey::kLargePages].PostStrings.Front(); |
| if (postString.IsEmpty()) |
| options.LargePages = true; |
| } |
| #endif |
| } |
| |
| 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 CArchiveCommandLineParser::Parse2(CArchiveCommandLineOptions &options) |
| { |
| const UStringVector &nonSwitchStrings = parser.NonSwitchStrings; |
| int numNonSwitchStrings = nonSwitchStrings.Size(); |
| if (numNonSwitchStrings < kMinNonSwitchWords) |
| ThrowUserErrorException(); |
| |
| if (!ParseArchiveCommand(nonSwitchStrings[kCommandIndex], options.Command)) |
| ThrowUserErrorException(); |
| |
| options.TechMode = parser[NKey::kTechMode].ThereIs; |
| options.CalcCrc = parser[NKey::kCalcCrc].ThereIs; |
| |
| if (parser[NKey::kCaseSensitive].ThereIs) |
| g_CaseSensitive = (parser[NKey::kCaseSensitive].PostCharIndex < 0); |
| |
| NRecursedType::EEnum recursedType; |
| if (parser[NKey::kRecursed].ThereIs) |
| recursedType = GetRecursedTypeFromIndex(parser[NKey::kRecursed].PostCharIndex); |
| else |
| recursedType = NRecursedType::kNonRecursed; |
| |
| UINT codePage = CP_ACP; |
| |
| bool thereAreSwitchIncludes = false; |
| if (parser[NKey::kInclude].ThereIs) |
| { |
| thereAreSwitchIncludes = true; |
| AddSwitchWildCardsToCensor(options.WildcardCensor, |
| parser[NKey::kInclude].PostStrings, true, recursedType, codePage); |
| } |
| if (parser[NKey::kExclude].ThereIs) |
| AddSwitchWildCardsToCensor(options.WildcardCensor, |
| parser[NKey::kExclude].PostStrings, false, recursedType, codePage); |
| |
| int curCommandIndex = kCommandIndex + 1; |
| bool thereIsArchiveName = !parser[NKey::kNoArName].ThereIs && |
| options.Command.CommandType != NCommandType::kBenchmark && |
| options.Command.CommandType != NCommandType::kInfo; |
| |
| bool isExtractGroupCommand = options.Command.IsFromExtractGroup(); |
| bool isExtractOrList = isExtractGroupCommand || options.Command.CommandType == NCommandType::kList; |
| |
| if (isExtractOrList && options.StdInMode) |
| thereIsArchiveName = false; |
| |
| if (thereIsArchiveName) |
| { |
| if (curCommandIndex >= numNonSwitchStrings) |
| ThrowUserErrorException(); |
| options.ArchiveName = nonSwitchStrings[curCommandIndex++]; |
| if (options.ArchiveName.IsEmpty()) |
| ThrowUserErrorException(); |
| } |
| |
| AddToCensorFromNonSwitchesStrings( |
| curCommandIndex, options.WildcardCensor, |
| nonSwitchStrings, recursedType, thereAreSwitchIncludes, codePage); |
| |
| options.YesToAll = parser[NKey::kYes].ThereIs; |
| |
| #ifdef ENV_HAVE_LSTAT |
| global_use_lstat = !parser[NKey::kUseLStat].ThereIs; |
| #endif |
| |
| #ifndef _NO_CRYPTO |
| options.PasswordEnabled = parser[NKey::kPassword].ThereIs; |
| if (options.PasswordEnabled) |
| options.Password = parser[NKey::kPassword].PostStrings[0]; |
| #endif |
| |
| options.ShowDialog = parser[NKey::kShowDialog].ThereIs; |
| |
| if (parser[NKey::kArchiveType].ThereIs) |
| options.ArcType = parser[NKey::kArchiveType].PostStrings[0]; |
| |
| if (isExtractOrList) |
| { |
| if (!options.WildcardCensor.AllAreRelative()) |
| ThrowException("Cannot use absolute pathnames for this command"); |
| |
| NWildcard::CCensor archiveWildcardCensor; |
| |
| if (parser[NKey::kArInclude].ThereIs) |
| AddSwitchWildCardsToCensor(archiveWildcardCensor, |
| parser[NKey::kArInclude].PostStrings, true, NRecursedType::kNonRecursed, codePage); |
| if (parser[NKey::kArExclude].ThereIs) |
| AddSwitchWildCardsToCensor(archiveWildcardCensor, |
| parser[NKey::kArExclude].PostStrings, false, NRecursedType::kNonRecursed, codePage); |
| |
| bool directlyAddArchiveName = false; |
| if (thereIsArchiveName) { |
| if ((options.ArchiveName.Find(kUniversalWildcard) == -1) && (options.ArchiveName.Find(L"?") == -1)) { |
| // no wildcard => no need to scan |
| directlyAddArchiveName = true; |
| } else { |
| AddNameToCensor(archiveWildcardCensor, options.ArchiveName, true, NRecursedType::kNonRecursed); |
| } |
| } |
| |
| #ifdef _WIN32 |
| ConvertToLongNames(archiveWildcardCensor); |
| #endif |
| |
| archiveWildcardCensor.ExtendExclude(); |
| |
| if (options.StdInMode) |
| { |
| UString arcName = parser[NKey::kStdIn].PostStrings.Front(); |
| options.ArchivePathsSorted.Add(arcName); |
| options.ArchivePathsFullSorted.Add(arcName); |
| } |
| else |
| { |
| |
| UStringVector archivePaths; |
| |
| { |
| CDirItems dirItems; |
| { |
| UStringVector errorPaths; |
| CRecordVector<DWORD> errorCodes; |
| HRESULT res = EnumerateItems(archiveWildcardCensor, dirItems, NULL, errorPaths, errorCodes); |
| if (res != S_OK || errorPaths.Size() > 0) |
| throw "cannot find archive"; |
| } |
| for (int i = 0; i < dirItems.Items.Size(); i++) |
| { |
| const CDirItem &dirItem = dirItems.Items[i]; |
| if (!dirItem.IsDir()) |
| archivePaths.Add(dirItems.GetPhyPath(i)); |
| } |
| } |
| |
| // Because the pathname of archive can be a symbolic link |
| // do not use "AddCommandLineWildCardToCensr(archiveWildcardCensor, options.ArchiveName" |
| if (directlyAddArchiveName) |
| archivePaths.Add(options.ArchiveName); |
| |
| if (archivePaths.Size() == 0) |
| throw "there is no such archive"; |
| |
| UStringVector archivePathsFull; |
| |
| int i; |
| for (i = 0; i < archivePaths.Size(); i++) |
| { |
| UString fullPath; |
| NFile::NDirectory::MyGetFullPathName(archivePaths[i], fullPath); |
| archivePathsFull.Add(fullPath); |
| } |
| CIntVector indices; |
| SortFileNames(archivePathsFull, indices); |
| options.ArchivePathsSorted.Reserve(indices.Size()); |
| options.ArchivePathsFullSorted.Reserve(indices.Size()); |
| for (i = 0; i < indices.Size(); i++) |
| { |
| options.ArchivePathsSorted.Add(archivePaths[indices[i]]); |
| options.ArchivePathsFullSorted.Add(archivePathsFull[indices[i]]); |
| } |
| |
| } |
| |
| if (isExtractGroupCommand) |
| { |
| SetMethodOptions(parser, options.ExtractProperties); |
| if (options.StdOutMode && options.IsStdOutTerminal && options.IsStdErrTerminal) |
| throw kSameTerminalError; |
| if (parser[NKey::kOutputDir].ThereIs) |
| { |
| options.OutputDir = parser[NKey::kOutputDir].PostStrings[0]; |
| NFile::NName::NormalizeDirPathPrefix(options.OutputDir); |
| } |
| |
| options.OverwriteMode = NExtract::NOverwriteMode::kAskBefore; |
| if (parser[NKey::kOverwrite].ThereIs) |
| options.OverwriteMode = k_OverwriteModes[parser[NKey::kOverwrite].PostCharIndex]; |
| else if (options.YesToAll) |
| options.OverwriteMode = NExtract::NOverwriteMode::kWithoutPrompt; |
| } |
| } |
| else if (options.Command.IsFromUpdateGroup()) |
| { |
| CUpdateOptions &updateOptions = options.UpdateOptions; |
| |
| SetAddCommandOptions(options.Command.CommandType, parser, updateOptions); |
| |
| SetMethodOptions(parser, updateOptions.MethodMode.Properties); |
| |
| options.EnablePercents = !parser[NKey::kDisablePercents].ThereIs; |
| |
| if (options.EnablePercents) |
| { |
| if ((options.StdOutMode && !options.IsStdErrTerminal) || |
| (!options.StdOutMode && !options.IsStdOutTerminal)) |
| options.EnablePercents = false; |
| } |
| |
| updateOptions.EMailMode = parser[NKey::kEmail].ThereIs; |
| if (updateOptions.EMailMode) |
| { |
| updateOptions.EMailAddress = parser[NKey::kEmail].PostStrings.Front(); |
| if (updateOptions.EMailAddress.Length() > 0) |
| if (updateOptions.EMailAddress[0] == L'.') |
| { |
| updateOptions.EMailRemoveAfter = true; |
| updateOptions.EMailAddress.Delete(0); |
| } |
| } |
| |
| updateOptions.StdOutMode = options.StdOutMode; |
| updateOptions.StdInMode = options.StdInMode; |
| |
| if (updateOptions.StdOutMode && updateOptions.EMailMode) |
| throw "stdout mode and email mode cannot be combined"; |
| if (updateOptions.StdOutMode && options.IsStdOutTerminal) |
| throw kTerminalOutError; |
| if (updateOptions.StdInMode) |
| updateOptions.StdInFileName = parser[NKey::kStdIn].PostStrings.Front(); |
| |
| #ifdef _WIN32 |
| ConvertToLongNames(options.WildcardCensor); |
| #endif |
| } |
| else if (options.Command.CommandType == NCommandType::kBenchmark) |
| { |
| options.NumThreads = (UInt32)-1; |
| options.DictionarySize = (UInt32)-1; |
| options.NumIterations = 1; |
| if (curCommandIndex < numNonSwitchStrings) |
| { |
| if (!ConvertStringToUInt32(nonSwitchStrings[curCommandIndex++], options.NumIterations)) |
| ThrowUserErrorException(); |
| } |
| for (int i = 0; i < parser[NKey::kProperty].PostStrings.Size(); i++) |
| { |
| UString postString = parser[NKey::kProperty].PostStrings[i]; |
| postString.MakeUpper(); |
| if (postString.Length() < 2) |
| ThrowUserErrorException(); |
| if (postString[0] == 'D') |
| { |
| int pos = 1; |
| if (postString[pos] == '=') |
| pos++; |
| UInt32 logSize; |
| if (!ConvertStringToUInt32((const wchar_t *)postString + pos, logSize)) |
| ThrowUserErrorException(); |
| if (logSize > 31) |
| ThrowUserErrorException(); |
| options.DictionarySize = 1 << logSize; |
| } |
| else if (postString[0] == 'M' && postString[1] == 'T' ) |
| { |
| int pos = 2; |
| if (postString[pos] == '=') |
| pos++; |
| if (postString[pos] != 0) |
| if (!ConvertStringToUInt32((const wchar_t *)postString + pos, options.NumThreads)) |
| ThrowUserErrorException(); |
| } |
| else if (postString[0] == 'M' && postString[1] == '=' ) |
| { |
| int pos = 2; |
| if (postString[pos] != 0) |
| options.Method = postString.Mid(2); |
| } |
| else |
| ThrowUserErrorException(); |
| } |
| } |
| else if (options.Command.CommandType == NCommandType::kInfo) |
| { |
| } |
| else |
| ThrowUserErrorException(); |
| options.WildcardCensor.ExtendExclude(); |
| } |