// ArchiveCommandLine.cpp | |
#include "StdAfx.h" | |
#undef printf | |
#undef sprintf | |
#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" | |
#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; | |
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; | |
} | |
static void AddNewLine(UString &s) | |
{ | |
s += L'\n'; | |
} | |
CArcCmdLineException::CArcCmdLineException(const char *a, const wchar_t *u) | |
{ | |
(*this) += MultiByteToUnicodeString(a); | |
if (u) | |
{ | |
AddNewLine(*this); | |
(*this) += u; | |
} | |
} | |
int g_CodePage = -1; | |
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, | |
kListfileCharSet, | |
kConsoleCharSet, | |
kTechMode, | |
kShareForWrite, | |
kCaseSensitive, | |
kHash, | |
kArcNameMode, | |
kDisableWildcardParsing, | |
kElimDup, | |
kFullPathMode, | |
kHardLinks, | |
kSymLinks, | |
kNtSecurity, | |
kAltStreams, | |
kReplaceColonForAltStream, | |
kWriteToAltStreamIfColon, | |
kDeleteAfterCompressing, | |
kSetArcMTime, | |
kExcludedArcType | |
}; | |
} | |
static const wchar_t kRecursedIDChar = 'r'; | |
static const char *kRecursedPostCharSet = "0-"; | |
static const char *k_ArcNameMode_PostCharSet = "sea"; | |
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, | |
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 char *kOverwritePostCharSet = "asut"; | |
NExtract::NOverwriteMode::EEnum k_OverwriteModes[] = | |
{ | |
NExtract::NOverwriteMode::kOverwrite, | |
NExtract::NOverwriteMode::kSkip, | |
NExtract::NOverwriteMode::kRename, | |
NExtract::NOverwriteMode::kRenameExisting | |
}; | |
static const CSwitchForm kSwitchForms[] = | |
{ | |
{ "?" }, | |
{ "h" }, | |
{ "-help" }, | |
{ "ba" }, | |
{ "bd" }, | |
{ "t", NSwitchType::kString, false, 1 }, | |
{ "y" }, | |
#ifndef _NO_CRYPTO | |
{ "p", NSwitchType::kString }, | |
#endif | |
{ "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 }, | |
{ "sfx", NSwitchType::kString }, | |
{ "si", NSwitchType::kString }, | |
{ "so" }, | |
{ "ao", NSwitchType::kChar, false, 1, kOverwritePostCharSet}, | |
{ "seml", NSwitchType::kString, false, 0}, | |
{ "ad" }, | |
{ "slp", NSwitchType::kMinus }, | |
{ "scs", NSwitchType::kString }, | |
{ "scc", NSwitchType::kString }, | |
{ "slt" }, | |
{ "ssw" }, | |
{ "ssc", NSwitchType::kMinus }, | |
{ "scrc", NSwitchType::kString, true, 0 }, | |
{ "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" }, | |
{ "stx", NSwitchType::kString, true, 1 } | |
}; | |
static const wchar_t *kUniversalWildcard = L"*"; | |
static const int kMinNonSwitchWords = 1; | |
static const int kCommandIndex = 0; | |
// 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 const char *kCannotFindArchive = "Cannot find archive"; | |
bool CArcCommand::IsFromExtractGroup() const | |
{ | |
switch (CommandType) | |
{ | |
case NCommandType::kTest: | |
case NCommandType::kExtract: | |
case NCommandType::kExtractFull: | |
return true; | |
} | |
return false; | |
} | |
NExtract::NPathMode::EEnum CArcCommand::GetPathMode() const | |
{ | |
switch (CommandType) | |
{ | |
case NCommandType::kTest: | |
case NCommandType::kExtractFull: | |
return NExtract::NPathMode::kFullPaths; | |
} | |
return NExtract::NPathMode::kNoPaths; | |
} | |
bool CArcCommand::IsFromUpdateGroup() const | |
{ | |
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::kNoRecursion: | |
return NRecursedType::kNonRecursed; | |
default: | |
return NRecursedType::kRecursed; | |
} | |
} | |
static const char *g_Commands = "audtexlbih"; | |
static bool ParseArchiveCommand(const UString &commandString, CArcCommand &command) | |
{ | |
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 &censor, | |
const UString &name, bool include, NRecursedType::EEnum type, bool wildcardMatching) | |
{ | |
bool recursed = false; | |
switch (type) | |
{ | |
case NRecursedType::kWildcardOnlyRecursed: | |
recursed = DoesNameContainWildcard(name); | |
break; | |
case NRecursedType::kRecursed: | |
recursed = true; | |
break; | |
} | |
censor.AddPreItem(include, name, recursed, wildcardMatching); | |
} | |
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; | |
AddNewLine(val); | |
val += pair.NewName; | |
AddNewLine(val); | |
if (type == NRecursedType::kRecursed) | |
val += L"-r"; | |
else if (type == NRecursedType::kRecursed) | |
val += L"-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 CArcCmdLineException(kCannotFindListFile, fileName); | |
if (!ReadNamesFromListFile(us2fs(fileName), names, codePage)) | |
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( | |
CObjectVector<CRenamePair> *renamePairs, | |
unsigned startIndex, | |
NWildcard::CCensor &censor, | |
const UStringVector &nonSwitchStrings, NRecursedType::EEnum type, | |
bool wildcardMatching, | |
bool thereAreSwitchIncludes, Int32 codePage) | |
{ | |
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 CArcCmdLineException(kEmptyFilePath); | |
if (s[0] == kFileListID) | |
AddToCensorFromListFile(renamePairs, censor, s.Ptr(1), true, type, wildcardMatching, codePage); | |
else if (renamePairs) | |
{ | |
if (oldIndex == -1) | |
oldIndex = startIndex; | |
else | |
{ | |
// 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; | |
} | |
} | |
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(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 + (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 &censor, | |
const UStringVector &strings, bool include, | |
NRecursedType::EEnum commonRecursedType, | |
bool wildcardMatching, | |
Int32 codePage) | |
{ | |
const char *errorMessage = NULL; | |
unsigned i; | |
for (i = 0; i < strings.Size(); i++) | |
{ | |
const UString &name = strings[i]; | |
NRecursedType::EEnum recursedType; | |
unsigned pos = 0; | |
if (name.Len() < kSomeCludePostStringMinSize) | |
{ | |
errorMessage = "Too short switch"; | |
break; | |
} | |
if (::MyCharLower_Ascii(name[pos]) == kRecursedIDChar) | |
{ | |
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.Len() < pos + kSomeCludeAfterRecursedPostStringMinSize) | |
{ | |
errorMessage = "Too short switch"; | |
break; | |
} | |
UString tail = name.Ptr(pos + 1); | |
if (name[pos] == kImmediateNameID) | |
AddNameToCensor(censor, tail, include, recursedType, wildcardMatching); | |
else if (name[pos] == kFileListID) | |
AddToCensorFromListFile(NULL, censor, tail, include, recursedType, wildcardMatching, codePage); | |
#ifdef _WIN32 | |
else if (name[pos] == kMapNameID) | |
{ | |
errorMessage = ParseMapWithPaths(censor, tail, include, recursedType, wildcardMatching); | |
if (errorMessage) | |
break; | |
} | |
#endif | |
else | |
{ | |
errorMessage = "Incorrect wildcarc type marker"; | |
break; | |
} | |
} | |
if (i != strings.Size()) | |
throw CArcCmdLineException(errorMessage, strings[i]); | |
} | |
#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::CFileInfo fi; | |
const FString path = us2fs(prefix + name); | |
if (NFile::NName::IsDevicePath(path)) | |
return; | |
if (fi.Find(path)) | |
name = fs2us(fi.Name); | |
} | |
static void ConvertToLongNames(const UString &prefix, CObjectVector<NWildcard::CItem> &items) | |
{ | |
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()); | |
} | |
} | |
static void ConvertToLongNames(const UString &prefix, NWildcard::CCensorNode &node) | |
{ | |
ConvertToLongNames(prefix, node.IncludeItems); | |
ConvertToLongNames(prefix, node.ExcludeItems); | |
unsigned i; | |
for (i = 0; i < node.SubNodes.Size(); i++) | |
{ | |
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 (unsigned j = i + 1; j < node.SubNodes.Size();) | |
{ | |
const NWildcard::CCensorNode &nextNode2 = node.SubNodes[j]; | |
if (nextNode1.Name.IsEqualToNoCase(nextNode2.Name)) | |
{ | |
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_PATH_SEPARATOR, nextNode); | |
} | |
} | |
void ConvertToLongNames(NWildcard::CCensor &censor) | |
{ | |
FOR_VECTOR (i, censor.Pairs) | |
{ | |
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; | |
} | |
*/ | |
static const wchar_t *kUpdatePairStateIDSet = L"pqrxyzw"; | |
static const int kUpdatePairStateNotSupportedActions[] = {2, 2, 1, -1, -1, -1, -1}; | |
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 (unsigned i = 0; i < command.Len();) | |
{ | |
wchar_t c = MyCharLower_Ascii(command[i]); | |
int statePos = FindCharPosInString(kUpdatePairStateIDSet, c); | |
if (statePos < 0) | |
{ | |
postString = command.Ptr(i); | |
return true; | |
} | |
i++; | |
if (i >= command.Len()) | |
return false; | |
c = command[i]; | |
if (c < '0' || c >= '0' + kNumUpdatePairActions) | |
return false; | |
int actionPos = c - '0'; | |
actionSet.StateActions[statePos] = (NUpdateArchive::NPairAction::EEnum)(actionPos); | |
if (kUpdatePairStateNotSupportedActions[statePos] == actionPos) | |
return false; | |
i++; | |
} | |
postString.Empty(); | |
return true; | |
} | |
static void ParseUpdateCommandString(CUpdateOptions &options, | |
const UStringVector &updatePostStrings, | |
const NUpdateArchive::CActionSet &defaultActionSet) | |
{ | |
const char *errorMessage = "incorrect update switch command"; | |
unsigned i; | |
for (i = 0; i < updatePostStrings.Size(); i++) | |
{ | |
const UString &updateString = updatePostStrings[i]; | |
if (updateString.IsEqualTo(kUpdateIgnoreItselfPostStringID)) | |
{ | |
if (options.UpdateArchiveItself) | |
{ | |
options.UpdateArchiveItself = false; | |
options.Commands.Delete(0); | |
} | |
} | |
else | |
{ | |
NUpdateArchive::CActionSet actionSet = defaultActionSet; | |
UString postString; | |
if (!ParseUpdateCommandString2(updateString, actionSet, postString)) | |
break; | |
if (postString.IsEmpty()) | |
{ | |
if (options.UpdateArchiveItself) | |
options.Commands[0].ActionSet = actionSet; | |
} | |
else | |
{ | |
if (postString[0] != kUpdateNewArchivePostCharID) | |
break; | |
CUpdateArchiveCommand uc; | |
UString archivePath = postString.Ptr(1); | |
if (archivePath.IsEmpty()) | |
break; | |
uc.UserArchivePath = archivePath; | |
uc.ActionSet = actionSet; | |
options.Commands.Add(uc); | |
} | |
} | |
} | |
if (i != updatePostStrings.Size()) | |
throw CArcCmdLineException(errorMessage, updatePostStrings[i]); | |
} | |
bool ParseComplexSize(const wchar_t *s, UInt64 &result); | |
static void SetAddCommandOptions( | |
NCommandType::EEnum commandType, | |
const CParser &parser, | |
CUpdateOptions &options) | |
{ | |
NUpdateArchive::CActionSet defaultActionSet; | |
switch (commandType) | |
{ | |
case NCommandType::kAdd: | |
defaultActionSet = NUpdateArchive::k_ActionSet_Add; | |
break; | |
case NCommandType::kDelete: | |
defaultActionSet = NUpdateArchive::k_ActionSet_Delete; | |
break; | |
default: | |
defaultActionSet = NUpdateArchive::k_ActionSet_Update; | |
} | |
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()) | |
NDir::MyGetTempPath(options.WorkingDir); | |
else | |
options.WorkingDir = us2fs(postString); | |
} | |
options.SfxMode = parser[NKey::kSfx].ThereIs; | |
if (options.SfxMode) | |
options.SfxModule = us2fs(parser[NKey::kSfx].PostStrings[0]); | |
if (parser[NKey::kVolume].ThereIs) | |
{ | |
const UStringVector &sv = parser[NKey::kVolume].PostStrings; | |
FOR_VECTOR (i, sv) | |
{ | |
UInt64 size; | |
if (!ParseComplexSize(sv[i], size) || size == 0) | |
throw CArcCmdLineException("Incorrect volume size:", sv[i]); | |
options.VolumesSizes.Add(size); | |
} | |
} | |
} | |
static void SetMethodOptions(const CParser &parser, CObjectVector<CProperty> &properties) | |
{ | |
if (parser[NKey::kProperty].ThereIs) | |
{ | |
FOR_VECTOR (i, parser[NKey::kProperty].PostStrings) | |
{ | |
CProperty prop; | |
prop.Name = parser[NKey::kProperty].PostStrings[i]; | |
int index = prop.Name.Find(L'='); | |
if (index >= 0) | |
{ | |
prop.Value = prop.Name.Ptr(index + 1); | |
prop.Name.DeleteFrom(index); | |
} | |
properties.Add(prop); | |
} | |
} | |
} | |
CArcCmdLineParser::CArcCmdLineParser(): parser(ARRAY_SIZE(kSwitchForms)) {} | |
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.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; | |
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; | |
} | |
#endif | |
} | |
struct CCodePagePair | |
{ | |
const char *Name; | |
Int32 CodePage; | |
}; | |
static const unsigned kNumByteOnlyCodePages = 3; | |
static CCodePagePair g_CodePagePairs[] = | |
{ | |
{ "utf-8", CP_UTF8 }, | |
{ "win", CP_ACP }, | |
{ "dos", CP_OEMCP }, | |
{ "utf-16le", MY__CP_UTF16 }, | |
{ "utf-16be", MY__CP_UTF16BE } | |
}; | |
static Int32 FindCharset(const NCommandLineParser::CParser &parser, int keyIndex, | |
bool byteOnlyCodePages, Int32 defaultVal) | |
{ | |
if (!parser[keyIndex].ThereIs) | |
return defaultVal; | |
UString name = parser[keyIndex].PostStrings.Back(); | |
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.IsEqualTo(pair.Name)) | |
return pair.CodePage; | |
} | |
} | |
void EnumerateDirItemsAndSort( | |
bool storeAltStreams, | |
NWildcard::CCensor &censor, | |
NWildcard::ECensorPathMode censorPathMode, | |
const UString &addPathPrefix, | |
UStringVector &sortedPaths, | |
UStringVector &sortedFullPaths) | |
{ | |
UStringVector paths; | |
{ | |
CDirItems dirItems; | |
{ | |
dirItems.ScanAltStreams = storeAltStreams; | |
HRESULT res = EnumerateItems(censor, censorPathMode, addPathPrefix, dirItems, NULL); | |
if (res != S_OK || dirItems.ErrorPaths.Size() > 0) | |
{ | |
UString errorPath; | |
if (dirItems.ErrorPaths.Size() > 0) | |
errorPath = fs2us(dirItems.ErrorPaths[0]); | |
throw CArcCmdLineException(kCannotFindArchive, | |
dirItems.ErrorPaths.Size() > 0 ? (const wchar_t *)errorPath : NULL); | |
} | |
} | |
FOR_VECTOR (i, dirItems.Items) | |
{ | |
const CDirItem &dirItem = dirItems.Items[i]; | |
if (!dirItem.IsDir()) | |
paths.Add(dirItems.GetPhyPath(i)); | |
} | |
} | |
if (paths.Size() == 0) | |
throw CArcCmdLineException(kCannotFindArchive); | |
UStringVector fullPaths; | |
unsigned i; | |
for (i = 0; i < paths.Size(); i++) | |
{ | |
FString fullPath; | |
NFile::NDir::MyGetFullPathName(us2fs(paths[i]), fullPath); | |
fullPaths.Add(fs2us(fullPath)); | |
} | |
CUIntVector indices; | |
SortFileNames(fullPaths, indices); | |
sortedPaths.ClearAndReserve(indices.Size()); | |
sortedFullPaths.ClearAndReserve(indices.Size()); | |
for (i = 0; i < indices.Size(); i++) | |
{ | |
unsigned index = indices[i]; | |
sortedPaths.AddInReserved(paths[index]); | |
sortedFullPaths.AddInReserved(fullPaths[index]); | |
if (i > 0 && CompareFileNames(sortedFullPaths[i], sortedFullPaths[i - 1]) == 0) | |
throw CArcCmdLineException("Duplicate archive path:", sortedFullPaths[i]); | |
} | |
} | |
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(); | |
if (numNonSwitchStrings < kMinNonSwitchWords) | |
throw CArcCmdLineException("The command must be spcified"); | |
if (!ParseArchiveCommand(nonSwitchStrings[kCommandIndex], options.Command)) | |
throw CArcCmdLineException("Unsupported command:", nonSwitchStrings[kCommandIndex]); | |
options.TechMode = parser[NKey::kTechMode].ThereIs; | |
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) | |
recursedType = GetRecursedTypeFromIndex(parser[NKey::kRecursed].PostCharIndex); | |
else | |
recursedType = NRecursedType::kNonRecursed; | |
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.Censor, | |
parser[NKey::kInclude].PostStrings, true, recursedType, wildcardMatching, codePage); | |
} | |
if (parser[NKey::kExclude].ThereIs) | |
AddSwitchWildcardsToCensor(options.Censor, | |
parser[NKey::kExclude].PostStrings, false, recursedType, wildcardMatching, codePage); | |
int curCommandIndex = kCommandIndex + 1; | |
bool thereIsArchiveName = !parser[NKey::kNoArName].ThereIs && | |
options.Command.CommandType != NCommandType::kBenchmark && | |
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 || isRename) && options.StdInMode) | |
thereIsArchiveName = false; | |
if (parser[NKey::kArcNameMode].ThereIs) | |
options.UpdateOptions.ArcNameMode = ParseArcNameMode(parser[NKey::kArcNameMode].PostCharIndex); | |
if (thereIsArchiveName) | |
{ | |
if (curCommandIndex >= numNonSwitchStrings) | |
throw CArcCmdLineException("Cannot find archive name"); | |
options.ArchiveName = nonSwitchStrings[curCommandIndex++]; | |
if (options.ArchiveName.IsEmpty()) | |
throw CArcCmdLineException("Archive name cannot by empty"); | |
} | |
AddToCensorFromNonSwitchesStrings(isRename ? &options.UpdateOptions.RenamePairs : NULL, | |
curCommandIndex, options.Censor, | |
nonSwitchStrings, recursedType, wildcardMatching, | |
thereAreSwitchIncludes, codePage); | |
options.YesToAll = parser[NKey::kYes].ThereIs; | |
#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]; | |
options.ExcludedArcTypes = parser[NKey::kExcludedArcType].PostStrings; | |
SetMethodOptions(parser, options.Properties); | |
options.EnablePercents = !parser[NKey::kDisablePercents].ThereIs; | |
if (options.EnablePercents) | |
{ | |
if ((options.StdOutMode && !options.IsStdErrTerminal) || | |
(!options.StdOutMode && !options.IsStdOutTerminal)) | |
options.EnablePercents = false; | |
} | |
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) | |
{ | |
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; | |
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; | |
if (parser[NKey::kArInclude].ThereIs) | |
AddSwitchWildcardsToCensor(arcCensor, parser[NKey::kArInclude].PostStrings, true, NRecursedType::kNonRecursed, wildcardMatching, codePage); | |
if (parser[NKey::kArExclude].ThereIs) | |
AddSwitchWildcardsToCensor(arcCensor, parser[NKey::kArExclude].PostStrings, false, NRecursedType::kNonRecursed, wildcardMatching, codePage); | |
if (thereIsArchiveName) | |
AddNameToCensor(arcCensor, options.ArchiveName, true, NRecursedType::kNonRecursed, wildcardMatching); | |
arcCensor.AddPathsToCensor(NWildcard::k_RelatPath); | |
#ifdef _WIN32 | |
ConvertToLongNames(arcCensor); | |
#endif | |
arcCensor.ExtendExclude(); | |
if (options.StdInMode) | |
{ | |
UString arcName = parser[NKey::kStdIn].PostStrings.Front(); | |
options.ArchivePathsSorted.Add(arcName); | |
options.ArchivePathsFullSorted.Add(arcName); | |
} | |
else | |
{ | |
EnumerateDirItemsAndSort( | |
false, // scanAltStreams | |
arcCensor, | |
NWildcard::k_RelatPath, | |
UString(), // addPathPrefix | |
options.ArchivePathsSorted, | |
options.ArchivePathsFullSorted); | |
} | |
if (isExtractGroupCommand) | |
{ | |
if (options.StdOutMode && options.IsStdOutTerminal && options.IsStdErrTerminal) | |
throw CArcCmdLineException(kSameTerminalError); | |
if (parser[NKey::kOutputDir].ThereIs) | |
{ | |
eo.OutputDir = us2fs(parser[NKey::kOutputDir].PostStrings[0]); | |
NFile::NName::NormalizeDirPathPrefix(eo.OutputDir); | |
} | |
eo.OverwriteMode = NExtract::NOverwriteMode::kAsk; | |
if (parser[NKey::kOverwrite].ThereIs) | |
{ | |
eo.OverwriteMode = k_OverwriteModes[parser[NKey::kOverwrite].PostCharIndex]; | |
eo.OverwriteMode_Force = true; | |
} | |
else if (options.YesToAll) | |
{ | |
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); | |
updateOptions.MethodMode.Properties = options.Properties; | |
if (parser[NKey::kShareForWrite].ThereIs) | |
updateOptions.OpenShareForWrite = true; | |
updateOptions.PathMode = censorPathMode; | |
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.Len() > 0) | |
if (updateOptions.EMailAddress[0] == L'.') | |
{ | |
updateOptions.EMailRemoveAfter = true; | |
updateOptions.EMailAddress.Delete(0); | |
} | |
} | |
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 CArcCmdLineException("stdout mode and email mode cannot be combined"); | |
if (updateOptions.StdOutMode && options.IsStdOutTerminal) | |
throw CArcCmdLineException(kTerminalOutError); | |
if (updateOptions.StdInMode) | |
updateOptions.StdInFileName = parser[NKey::kStdIn].PostStrings.Front(); | |
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 (!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 | |
throw 9815676711; | |
} |