Home | History | Annotate | Download | only in Common
      1 // ArchiveCommandLine.cpp
      2 
      3 #include "StdAfx.h"
      4 #undef printf
      5 #undef sprintf
      6 
      7 #ifdef _WIN32
      8 #ifndef UNDER_CE
      9 #include <io.h>
     10 #endif
     11 #else
     12 // for isatty()
     13 #include <unistd.h>
     14 #endif
     15 
     16 #include <stdio.h>
     17 
     18 #ifdef _7ZIP_LARGE_PAGES
     19 #include "../../../../C/Alloc.h"
     20 #endif
     21 
     22 #include "../../../Common/ListFileUtils.h"
     23 #include "../../../Common/StringConvert.h"
     24 #include "../../../Common/StringToInt.h"
     25 
     26 #include "../../../Windows/ErrorMsg.h"
     27 #include "../../../Windows/FileDir.h"
     28 #include "../../../Windows/FileName.h"
     29 #ifdef _WIN32
     30 #include "../../../Windows/FileMapping.h"
     31 #include "../../../Windows/MemoryLock.h"
     32 #include "../../../Windows/Synchronization.h"
     33 #endif
     34 
     35 #include "ArchiveCommandLine.h"
     36 #include "EnumDirItems.h"
     37 #include "Update.h"
     38 #include "UpdateAction.h"
     39 
     40 extern bool g_CaseSensitive;
     41 extern bool g_PathTrailReplaceMode;
     42 
     43 #ifdef _7ZIP_LARGE_PAGES
     44 bool g_LargePagesMode = false;
     45 #endif
     46 
     47 #ifdef UNDER_CE
     48 
     49 #define MY_IS_TERMINAL(x) false;
     50 
     51 #else
     52 
     53 #if _MSC_VER >= 1400
     54 #define MY_isatty_fileno(x) _isatty(_fileno(x))
     55 #else
     56 #define MY_isatty_fileno(x) isatty(fileno(x))
     57 #endif
     58 
     59 #define MY_IS_TERMINAL(x) (MY_isatty_fileno(x) != 0);
     60 
     61 #endif
     62 
     63 using namespace NCommandLineParser;
     64 using namespace NWindows;
     65 using namespace NFile;
     66 
     67 static bool StringToUInt32(const wchar_t *s, UInt32 &v)
     68 {
     69   if (*s == 0)
     70     return false;
     71   const wchar_t *end;
     72   v = ConvertStringToUInt32(s, &end);
     73   return *end == 0;
     74 }
     75 
     76 
     77 int g_CodePage = -1;
     78 
     79 namespace NKey {
     80 enum Enum
     81 {
     82   kHelp1 = 0,
     83   kHelp2,
     84   kHelp3,
     85 
     86   kDisableHeaders,
     87   kDisablePercents,
     88   kShowTime,
     89   kLogLevel,
     90 
     91   kOutStream,
     92   kErrStream,
     93   kPercentStream,
     94 
     95   kYes,
     96 
     97   kShowDialog,
     98   kOverwrite,
     99 
    100   kArchiveType,
    101   kExcludedArcType,
    102 
    103   kProperty,
    104   kOutputDir,
    105   kWorkingDir,
    106 
    107   kInclude,
    108   kExclude,
    109   kArInclude,
    110   kArExclude,
    111   kNoArName,
    112 
    113   kUpdate,
    114   kVolume,
    115   kRecursed,
    116 
    117   kAffinity,
    118   kSfx,
    119   kEmail,
    120   kHash,
    121 
    122   kStdIn,
    123   kStdOut,
    124 
    125   kLargePages,
    126   kListfileCharSet,
    127   kConsoleCharSet,
    128   kTechMode,
    129 
    130   kShareForWrite,
    131   kStopAfterOpenError,
    132   kCaseSensitive,
    133   kArcNameMode,
    134 
    135   kDisableWildcardParsing,
    136   kElimDup,
    137   kFullPathMode,
    138 
    139   kHardLinks,
    140   kSymLinks,
    141   kNtSecurity,
    142 
    143   kAltStreams,
    144   kReplaceColonForAltStream,
    145   kWriteToAltStreamIfColon,
    146 
    147   kNameTrailReplace,
    148 
    149   kDeleteAfterCompressing,
    150   kSetArcMTime
    151 
    152   #ifndef _NO_CRYPTO
    153   , kPassword
    154   #endif
    155 };
    156 
    157 }
    158 
    159 
    160 static const wchar_t kRecursedIDChar = 'r';
    161 static const char * const kRecursedPostCharSet = "0-";
    162 
    163 static const char * const k_ArcNameMode_PostCharSet = "sea";
    164 
    165 static const char * const k_Stream_PostCharSet = "012";
    166 
    167 static inline const EArcNameMode ParseArcNameMode(int postCharIndex)
    168 {
    169   switch (postCharIndex)
    170   {
    171     case 1: return k_ArcNameMode_Exact;
    172     case 2: return k_ArcNameMode_Add;
    173     default: return k_ArcNameMode_Smart;
    174   }
    175 }
    176 
    177 namespace NRecursedPostCharIndex {
    178   enum EEnum
    179   {
    180     kWildcardRecursionOnly = 0,
    181     kNoRecursion = 1
    182   };
    183 }
    184 
    185 static const char kImmediateNameID = '!';
    186 static const char kMapNameID = '#';
    187 static const char kFileListID = '@';
    188 
    189 static const char kSomeCludePostStringMinSize = 2; // at least <@|!><N>ame must be
    190 static const char kSomeCludeAfterRecursedPostStringMinSize = 2; // at least <@|!><N>ame must be
    191 
    192 static const char * const kOverwritePostCharSet = "asut";
    193 
    194 static const NExtract::NOverwriteMode::EEnum k_OverwriteModes[] =
    195 {
    196   NExtract::NOverwriteMode::kOverwrite,
    197   NExtract::NOverwriteMode::kSkip,
    198   NExtract::NOverwriteMode::kRename,
    199   NExtract::NOverwriteMode::kRenameExisting
    200 };
    201 
    202 static const CSwitchForm kSwitchForms[] =
    203 {
    204   { "?" },
    205   { "h" },
    206   { "-help" },
    207 
    208   { "ba" },
    209   { "bd" },
    210   { "bt" },
    211   { "bb", NSwitchType::kString, false, 0 },
    212 
    213   { "bso", NSwitchType::kChar, false, 1, k_Stream_PostCharSet },
    214   { "bse", NSwitchType::kChar, false, 1, k_Stream_PostCharSet },
    215   { "bsp", NSwitchType::kChar, false, 1, k_Stream_PostCharSet },
    216 
    217   { "y" },
    218 
    219   { "ad" },
    220   { "ao", NSwitchType::kChar, false, 1, kOverwritePostCharSet},
    221 
    222   { "t",  NSwitchType::kString, false, 1 },
    223   { "stx", NSwitchType::kString, true, 1 },
    224 
    225   { "m",  NSwitchType::kString, true, 1 },
    226   { "o",  NSwitchType::kString, false, 1 },
    227   { "w",  NSwitchType::kString },
    228 
    229   { "i",  NSwitchType::kString, true, kSomeCludePostStringMinSize},
    230   { "x",  NSwitchType::kString, true, kSomeCludePostStringMinSize},
    231   { "ai", NSwitchType::kString, true, kSomeCludePostStringMinSize},
    232   { "ax", NSwitchType::kString, true, kSomeCludePostStringMinSize},
    233   { "an" },
    234 
    235   { "u",  NSwitchType::kString, true, 1},
    236   { "v",  NSwitchType::kString, true, 1},
    237   { "r",  NSwitchType::kChar, false, 0, kRecursedPostCharSet },
    238 
    239   { "stm", NSwitchType::kString },
    240   { "sfx", NSwitchType::kString },
    241   { "seml", NSwitchType::kString, false, 0},
    242   { "scrc", NSwitchType::kString, true, 0 },
    243 
    244   { "si", NSwitchType::kString },
    245   { "so" },
    246 
    247   { "slp", NSwitchType::kString },
    248   { "scs", NSwitchType::kString },
    249   { "scc", NSwitchType::kString },
    250   { "slt" },
    251 
    252   { "ssw" },
    253   { "sse" },
    254   { "ssc", NSwitchType::kMinus },
    255   { "sa",  NSwitchType::kChar, false, 1, k_ArcNameMode_PostCharSet },
    256 
    257   { "spd" },
    258   { "spe", NSwitchType::kMinus },
    259   { "spf", NSwitchType::kString, false, 0 },
    260 
    261   { "snh", NSwitchType::kMinus },
    262   { "snl", NSwitchType::kMinus },
    263   { "sni" },
    264 
    265   { "sns", NSwitchType::kMinus },
    266   { "snr" },
    267   { "snc" },
    268 
    269   { "snt", NSwitchType::kMinus },
    270 
    271   { "sdel" },
    272   { "stl" }
    273 
    274   #ifndef _NO_CRYPTO
    275   , { "p",  NSwitchType::kString }
    276   #endif
    277 };
    278 
    279 static const char * const kUniversalWildcard = "*";
    280 static const unsigned kMinNonSwitchWords = 1;
    281 static const unsigned kCommandIndex = 0;
    282 
    283 // static const char * const kUserErrorMessage  = "Incorrect command line";
    284 static const char * const kCannotFindListFile = "Cannot find listfile";
    285 static const char * const kIncorrectListFile = "Incorrect item in listfile.\nCheck charset encoding and -scs switch.";
    286 static const char * const kTerminalOutError = "I won't write compressed data to a terminal";
    287 static const char * const kSameTerminalError = "I won't write data and program's messages to same stream";
    288 static const char * const kEmptyFilePath = "Empty file path";
    289 
    290 bool CArcCommand::IsFromExtractGroup() const
    291 {
    292   switch (CommandType)
    293   {
    294     case NCommandType::kTest:
    295     case NCommandType::kExtract:
    296     case NCommandType::kExtractFull:
    297       return true;
    298   }
    299   return false;
    300 }
    301 
    302 NExtract::NPathMode::EEnum CArcCommand::GetPathMode() const
    303 {
    304   switch (CommandType)
    305   {
    306     case NCommandType::kTest:
    307     case NCommandType::kExtractFull:
    308       return NExtract::NPathMode::kFullPaths;
    309   }
    310   return NExtract::NPathMode::kNoPaths;
    311 }
    312 
    313 bool CArcCommand::IsFromUpdateGroup() const
    314 {
    315   switch (CommandType)
    316   {
    317     case NCommandType::kAdd:
    318     case NCommandType::kUpdate:
    319     case NCommandType::kDelete:
    320     case NCommandType::kRename:
    321       return true;
    322   }
    323   return false;
    324 }
    325 
    326 static NRecursedType::EEnum GetRecursedTypeFromIndex(int index)
    327 {
    328   switch (index)
    329   {
    330     case NRecursedPostCharIndex::kWildcardRecursionOnly:
    331       return NRecursedType::kWildcardOnlyRecursed;
    332     case NRecursedPostCharIndex::kNoRecursion:
    333       return NRecursedType::kNonRecursed;
    334     default:
    335       return NRecursedType::kRecursed;
    336   }
    337 }
    338 
    339 static const char *g_Commands = "audtexlbih";
    340 
    341 static bool ParseArchiveCommand(const UString &commandString, CArcCommand &command)
    342 {
    343   UString s (commandString);
    344   s.MakeLower_Ascii();
    345   if (s.Len() == 1)
    346   {
    347     if (s[0] > 0x7F)
    348       return false;
    349     int index = FindCharPosInString(g_Commands, (char)s[0]);
    350     if (index < 0)
    351       return false;
    352     command.CommandType = (NCommandType::EEnum)index;
    353     return true;
    354   }
    355   if (s.Len() == 2 && s[0] == 'r' && s[1] == 'n')
    356   {
    357     command.CommandType = (NCommandType::kRename);
    358     return true;
    359   }
    360   return false;
    361 }
    362 
    363 // ------------------------------------------------------------------
    364 // filenames functions
    365 
    366 static void AddNameToCensor(NWildcard::CCensor &censor,
    367     const UString &name, bool include, NRecursedType::EEnum type, bool wildcardMatching)
    368 {
    369   bool recursed = false;
    370 
    371   switch (type)
    372   {
    373     case NRecursedType::kWildcardOnlyRecursed:
    374       recursed = DoesNameContainWildcard(name);
    375       break;
    376     case NRecursedType::kRecursed:
    377       recursed = true;
    378       break;
    379   }
    380   censor.AddPreItem(include, name, recursed, wildcardMatching);
    381 }
    382 
    383 static void AddRenamePair(CObjectVector<CRenamePair> *renamePairs,
    384     const UString &oldName, const UString &newName, NRecursedType::EEnum type,
    385     bool wildcardMatching)
    386 {
    387   CRenamePair &pair = renamePairs->AddNew();
    388   pair.OldName = oldName;
    389   pair.NewName = newName;
    390   pair.RecursedType = type;
    391   pair.WildcardParsing = wildcardMatching;
    392 
    393   if (!pair.Prepare())
    394   {
    395     UString val;
    396     val += pair.OldName;
    397     val.Add_LF();
    398     val += pair.NewName;
    399     val.Add_LF();
    400     if (type == NRecursedType::kRecursed)
    401       val += "-r";
    402     else if (type == NRecursedType::kWildcardOnlyRecursed)
    403       val += "-r0";
    404     throw CArcCmdLineException("Unsupported rename command:", val);
    405   }
    406 }
    407 
    408 static void AddToCensorFromListFile(
    409     CObjectVector<CRenamePair> *renamePairs,
    410     NWildcard::CCensor &censor,
    411     LPCWSTR fileName, bool include, NRecursedType::EEnum type, bool wildcardMatching, Int32 codePage)
    412 {
    413   UStringVector names;
    414   if (!NFind::DoesFileExist(us2fs(fileName)))
    415     throw CArcCmdLineException(kCannotFindListFile, fileName);
    416   DWORD lastError = 0;
    417   if (!ReadNamesFromListFile2(us2fs(fileName), names, codePage, lastError))
    418   {
    419     if (lastError != 0)
    420     {
    421       UString m;
    422       m = "The file operation error for listfile";
    423       m.Add_LF();
    424       m += NError::MyFormatMessage(lastError);
    425       throw CArcCmdLineException(m, fileName);
    426     }
    427     throw CArcCmdLineException(kIncorrectListFile, fileName);
    428   }
    429   if (renamePairs)
    430   {
    431     if ((names.Size() & 1) != 0)
    432       throw CArcCmdLineException(kIncorrectListFile, fileName);
    433     for (unsigned i = 0; i < names.Size(); i += 2)
    434     {
    435       // change type !!!!
    436       AddRenamePair(renamePairs, names[i], names[i + 1], type, wildcardMatching);
    437     }
    438   }
    439   else
    440     FOR_VECTOR (i, names)
    441       AddNameToCensor(censor, names[i], include, type, wildcardMatching);
    442 }
    443 
    444 static void AddToCensorFromNonSwitchesStrings(
    445     CObjectVector<CRenamePair> *renamePairs,
    446     unsigned startIndex,
    447     NWildcard::CCensor &censor,
    448     const UStringVector &nonSwitchStrings,
    449     int stopSwitchIndex,
    450     NRecursedType::EEnum type,
    451     bool wildcardMatching,
    452     bool thereAreSwitchIncludes, Int32 codePage)
    453 {
    454   if ((renamePairs || nonSwitchStrings.Size() == startIndex) && !thereAreSwitchIncludes)
    455     AddNameToCensor(censor, UString(kUniversalWildcard), true, type,
    456         true // wildcardMatching
    457         );
    458 
    459   int oldIndex = -1;
    460 
    461   if (stopSwitchIndex < 0)
    462     stopSwitchIndex = nonSwitchStrings.Size();
    463 
    464   for (unsigned i = startIndex; i < nonSwitchStrings.Size(); i++)
    465   {
    466     const UString &s = nonSwitchStrings[i];
    467     if (s.IsEmpty())
    468       throw CArcCmdLineException(kEmptyFilePath);
    469     if (i < (unsigned)stopSwitchIndex && s[0] == kFileListID)
    470       AddToCensorFromListFile(renamePairs, censor, s.Ptr(1), true, type, wildcardMatching, codePage);
    471     else if (renamePairs)
    472     {
    473       if (oldIndex == -1)
    474         oldIndex = i;
    475       else
    476       {
    477         // NRecursedType::EEnum type is used for global wildcard (-i! switches)
    478         AddRenamePair(renamePairs, nonSwitchStrings[oldIndex], s, NRecursedType::kNonRecursed, wildcardMatching);
    479         // AddRenamePair(renamePairs, nonSwitchStrings[oldIndex], s, type);
    480         oldIndex = -1;
    481       }
    482     }
    483     else
    484       AddNameToCensor(censor, s, true, type, wildcardMatching);
    485   }
    486 
    487   if (oldIndex != -1)
    488   {
    489     throw CArcCmdLineException("There is no second file name for rename pair:", nonSwitchStrings[oldIndex]);
    490   }
    491 }
    492 
    493 #ifdef _WIN32
    494 
    495 struct CEventSetEnd
    496 {
    497   UString Name;
    498 
    499   CEventSetEnd(const wchar_t *name): Name(name) {}
    500   ~CEventSetEnd()
    501   {
    502     NSynchronization::CManualResetEvent event;
    503     if (event.Open(EVENT_MODIFY_STATE, false, GetSystemString(Name)) == 0)
    504       event.Set();
    505   }
    506 };
    507 
    508 static const char * const k_IncorrectMapCommand = "Incorrect Map command";
    509 
    510 static const char *ParseMapWithPaths(
    511     NWildcard::CCensor &censor,
    512     const UString &s2, bool include,
    513     NRecursedType::EEnum commonRecursedType,
    514     bool wildcardMatching)
    515 {
    516   UString s (s2);
    517   int pos = s.Find(L':');
    518   if (pos < 0)
    519     return k_IncorrectMapCommand;
    520   int pos2 = s.Find(L':', pos + 1);
    521   if (pos2 < 0)
    522     return k_IncorrectMapCommand;
    523 
    524   CEventSetEnd eventSetEnd((const wchar_t *)s + ((unsigned)pos2 + 1));
    525   s.DeleteFrom(pos2);
    526   UInt32 size;
    527   if (!StringToUInt32(s.Ptr(pos + 1), size)
    528       || size < sizeof(wchar_t)
    529       || size > ((UInt32)1 << 31)
    530       || size % sizeof(wchar_t) != 0)
    531     return "Unsupported Map data size";
    532 
    533   s.DeleteFrom(pos);
    534   CFileMapping map;
    535   if (map.Open(FILE_MAP_READ, GetSystemString(s)) != 0)
    536     return "Can not open mapping";
    537   LPVOID data = map.Map(FILE_MAP_READ, 0, size);
    538   if (!data)
    539     return "MapViewOfFile error";
    540   CFileUnmapper unmapper(data);
    541 
    542   UString name;
    543   const wchar_t *p = (const wchar_t *)data;
    544   if (*p != 0) // data format marker
    545     return "Unsupported Map data";
    546   UInt32 numChars = size / sizeof(wchar_t);
    547   for (UInt32 i = 1; i < numChars; i++)
    548   {
    549     wchar_t c = p[i];
    550     if (c == 0)
    551     {
    552       // MessageBoxW(0, name, L"7-Zip", 0);
    553       AddNameToCensor(censor, name, include, commonRecursedType, wildcardMatching);
    554       name.Empty();
    555     }
    556     else
    557       name += c;
    558   }
    559   if (!name.IsEmpty())
    560     return "Map data error";
    561 
    562   return NULL;
    563 }
    564 
    565 #endif
    566 
    567 static void AddSwitchWildcardsToCensor(
    568     NWildcard::CCensor &censor,
    569     const UStringVector &strings, bool include,
    570     NRecursedType::EEnum commonRecursedType,
    571     bool wildcardMatching,
    572     Int32 codePage)
    573 {
    574   const char *errorMessage = NULL;
    575   unsigned i;
    576   for (i = 0; i < strings.Size(); i++)
    577   {
    578     const UString &name = strings[i];
    579     NRecursedType::EEnum recursedType;
    580     unsigned pos = 0;
    581 
    582     if (name.Len() < kSomeCludePostStringMinSize)
    583     {
    584       errorMessage = "Too short switch";
    585       break;
    586     }
    587 
    588     if (::MyCharLower_Ascii(name[pos]) == kRecursedIDChar)
    589     {
    590       pos++;
    591       wchar_t c = name[pos];
    592       int index = -1;
    593       if (c <= 0x7F)
    594         index = FindCharPosInString(kRecursedPostCharSet, (char)c);
    595       recursedType = GetRecursedTypeFromIndex(index);
    596       if (index >= 0)
    597         pos++;
    598     }
    599     else
    600       recursedType = commonRecursedType;
    601 
    602     if (name.Len() < pos + kSomeCludeAfterRecursedPostStringMinSize)
    603     {
    604       errorMessage = "Too short switch";
    605       break;
    606     }
    607 
    608     const UString tail = name.Ptr(pos + 1);
    609 
    610     if (name[pos] == kImmediateNameID)
    611       AddNameToCensor(censor, tail, include, recursedType, wildcardMatching);
    612     else if (name[pos] == kFileListID)
    613       AddToCensorFromListFile(NULL, censor, tail, include, recursedType, wildcardMatching, codePage);
    614     #ifdef _WIN32
    615     else if (name[pos] == kMapNameID)
    616     {
    617       errorMessage = ParseMapWithPaths(censor, tail, include, recursedType, wildcardMatching);
    618       if (errorMessage)
    619         break;
    620     }
    621     #endif
    622     else
    623     {
    624       errorMessage = "Incorrect wildcard type marker";
    625       break;
    626     }
    627   }
    628   if (i != strings.Size())
    629     throw CArcCmdLineException(errorMessage, strings[i]);
    630 }
    631 
    632 /*
    633 static NUpdateArchive::NPairAction::EEnum GetUpdatePairActionType(int i)
    634 {
    635   switch (i)
    636   {
    637     case NUpdateArchive::NPairAction::kIgnore: return NUpdateArchive::NPairAction::kIgnore;
    638     case NUpdateArchive::NPairAction::kCopy: return NUpdateArchive::NPairAction::kCopy;
    639     case NUpdateArchive::NPairAction::kCompress: return NUpdateArchive::NPairAction::kCompress;
    640     case NUpdateArchive::NPairAction::kCompressAsAnti: return NUpdateArchive::NPairAction::kCompressAsAnti;
    641   }
    642   throw 98111603;
    643 }
    644 */
    645 
    646 static const char * const kUpdatePairStateIDSet = "pqrxyzw";
    647 static const int kUpdatePairStateNotSupportedActions[] = {2, 2, 1, -1, -1, -1, -1};
    648 
    649 static const unsigned kNumUpdatePairActions = 4;
    650 static const char * const kUpdateIgnoreItselfPostStringID = "-";
    651 static const wchar_t kUpdateNewArchivePostCharID = '!';
    652 
    653 
    654 static bool ParseUpdateCommandString2(const UString &command,
    655     NUpdateArchive::CActionSet &actionSet, UString &postString)
    656 {
    657   for (unsigned i = 0; i < command.Len();)
    658   {
    659     wchar_t c = MyCharLower_Ascii(command[i]);
    660     int statePos = FindCharPosInString(kUpdatePairStateIDSet, (char)c);
    661     if (c > 0x7F || statePos < 0)
    662     {
    663       postString = command.Ptr(i);
    664       return true;
    665     }
    666     i++;
    667     if (i >= command.Len())
    668       return false;
    669     c = command[i];
    670     if (c < '0' || c >= '0' + kNumUpdatePairActions)
    671       return false;
    672     unsigned actionPos = c - '0';
    673     actionSet.StateActions[(unsigned)statePos] = (NUpdateArchive::NPairAction::EEnum)(actionPos);
    674     if (kUpdatePairStateNotSupportedActions[(unsigned)statePos] == (int)actionPos)
    675       return false;
    676     i++;
    677   }
    678   postString.Empty();
    679   return true;
    680 }
    681 
    682 static void ParseUpdateCommandString(CUpdateOptions &options,
    683     const UStringVector &updatePostStrings,
    684     const NUpdateArchive::CActionSet &defaultActionSet)
    685 {
    686   const char *errorMessage = "incorrect update switch command";
    687   unsigned i;
    688   for (i = 0; i < updatePostStrings.Size(); i++)
    689   {
    690     const UString &updateString = updatePostStrings[i];
    691     if (updateString.IsEqualTo(kUpdateIgnoreItselfPostStringID))
    692     {
    693       if (options.UpdateArchiveItself)
    694       {
    695         options.UpdateArchiveItself = false;
    696         options.Commands.Delete(0);
    697       }
    698     }
    699     else
    700     {
    701       NUpdateArchive::CActionSet actionSet = defaultActionSet;
    702 
    703       UString postString;
    704       if (!ParseUpdateCommandString2(updateString, actionSet, postString))
    705         break;
    706       if (postString.IsEmpty())
    707       {
    708         if (options.UpdateArchiveItself)
    709           options.Commands[0].ActionSet = actionSet;
    710       }
    711       else
    712       {
    713         if (postString[0] != kUpdateNewArchivePostCharID)
    714           break;
    715         CUpdateArchiveCommand uc;
    716         UString archivePath = postString.Ptr(1);
    717         if (archivePath.IsEmpty())
    718           break;
    719         uc.UserArchivePath = archivePath;
    720         uc.ActionSet = actionSet;
    721         options.Commands.Add(uc);
    722       }
    723     }
    724   }
    725   if (i != updatePostStrings.Size())
    726     throw CArcCmdLineException(errorMessage, updatePostStrings[i]);
    727 }
    728 
    729 bool ParseComplexSize(const wchar_t *s, UInt64 &result);
    730 
    731 static void SetAddCommandOptions(
    732     NCommandType::EEnum commandType,
    733     const CParser &parser,
    734     CUpdateOptions &options)
    735 {
    736   NUpdateArchive::CActionSet defaultActionSet;
    737   switch (commandType)
    738   {
    739     case NCommandType::kAdd:
    740       defaultActionSet = NUpdateArchive::k_ActionSet_Add;
    741       break;
    742     case NCommandType::kDelete:
    743       defaultActionSet = NUpdateArchive::k_ActionSet_Delete;
    744       break;
    745     default:
    746       defaultActionSet = NUpdateArchive::k_ActionSet_Update;
    747   }
    748 
    749   options.UpdateArchiveItself = true;
    750 
    751   options.Commands.Clear();
    752   CUpdateArchiveCommand updateMainCommand;
    753   updateMainCommand.ActionSet = defaultActionSet;
    754   options.Commands.Add(updateMainCommand);
    755   if (parser[NKey::kUpdate].ThereIs)
    756     ParseUpdateCommandString(options, parser[NKey::kUpdate].PostStrings,
    757         defaultActionSet);
    758   if (parser[NKey::kWorkingDir].ThereIs)
    759   {
    760     const UString &postString = parser[NKey::kWorkingDir].PostStrings[0];
    761     if (postString.IsEmpty())
    762       NDir::MyGetTempPath(options.WorkingDir);
    763     else
    764       options.WorkingDir = us2fs(postString);
    765   }
    766   options.SfxMode = parser[NKey::kSfx].ThereIs;
    767   if (options.SfxMode)
    768     options.SfxModule = us2fs(parser[NKey::kSfx].PostStrings[0]);
    769 
    770   if (parser[NKey::kVolume].ThereIs)
    771   {
    772     const UStringVector &sv = parser[NKey::kVolume].PostStrings;
    773     FOR_VECTOR (i, sv)
    774     {
    775       UInt64 size;
    776       if (!ParseComplexSize(sv[i], size) || size == 0)
    777         throw CArcCmdLineException("Incorrect volume size:", sv[i]);
    778       options.VolumesSizes.Add(size);
    779     }
    780   }
    781 }
    782 
    783 static void SetMethodOptions(const CParser &parser, CObjectVector<CProperty> &properties)
    784 {
    785   if (parser[NKey::kProperty].ThereIs)
    786   {
    787     FOR_VECTOR (i, parser[NKey::kProperty].PostStrings)
    788     {
    789       CProperty prop;
    790       prop.Name = parser[NKey::kProperty].PostStrings[i];
    791       int index = prop.Name.Find(L'=');
    792       if (index >= 0)
    793       {
    794         prop.Value = prop.Name.Ptr(index + 1);
    795         prop.Name.DeleteFrom(index);
    796       }
    797       properties.Add(prop);
    798     }
    799   }
    800 }
    801 
    802 
    803 static inline void SetStreamMode(const CSwitchResult &sw, unsigned &res)
    804 {
    805   if (sw.ThereIs)
    806     res = sw.PostCharIndex;
    807 }
    808 
    809 
    810 void CArcCmdLineParser::Parse1(const UStringVector &commandStrings,
    811     CArcCmdLineOptions &options)
    812 {
    813   if (!parser.ParseStrings(kSwitchForms, ARRAY_SIZE(kSwitchForms), commandStrings))
    814     throw CArcCmdLineException(parser.ErrorMessage, parser.ErrorLine);
    815 
    816   options.IsInTerminal = MY_IS_TERMINAL(stdin);
    817   options.IsStdOutTerminal = MY_IS_TERMINAL(stdout);
    818   options.IsStdErrTerminal = MY_IS_TERMINAL(stderr);
    819 
    820   options.HelpMode = parser[NKey::kHelp1].ThereIs || parser[NKey::kHelp2].ThereIs  || parser[NKey::kHelp3].ThereIs;
    821 
    822   options.StdInMode = parser[NKey::kStdIn].ThereIs;
    823   options.StdOutMode = parser[NKey::kStdOut].ThereIs;
    824   options.EnableHeaders = !parser[NKey::kDisableHeaders].ThereIs;
    825   options.TechMode = parser[NKey::kTechMode].ThereIs;
    826   options.ShowTime = parser[NKey::kShowTime].ThereIs;
    827 
    828   if (parser[NKey::kDisablePercents].ThereIs
    829       || options.StdOutMode
    830       || !options.IsStdOutTerminal)
    831     options.Number_for_Percents = k_OutStream_disabled;
    832 
    833   if (options.StdOutMode)
    834     options.Number_for_Out = k_OutStream_disabled;
    835 
    836   SetStreamMode(parser[NKey::kOutStream], options.Number_for_Out);
    837   SetStreamMode(parser[NKey::kErrStream], options.Number_for_Errors);
    838   SetStreamMode(parser[NKey::kPercentStream], options.Number_for_Percents);
    839 
    840   if (parser[NKey::kLogLevel].ThereIs)
    841   {
    842     const UString &s = parser[NKey::kLogLevel].PostStrings[0];
    843     if (s.IsEmpty())
    844       options.LogLevel = 1;
    845     else
    846     {
    847       UInt32 v;
    848       if (!StringToUInt32(s, v))
    849         throw CArcCmdLineException("Unsupported switch postfix -bb", s);
    850       options.LogLevel = (unsigned)v;
    851     }
    852   }
    853 
    854   if (parser[NKey::kCaseSensitive].ThereIs)
    855   {
    856     g_CaseSensitive = !parser[NKey::kCaseSensitive].WithMinus;
    857     options.CaseSensitiveChange = true;
    858     options.CaseSensitive = g_CaseSensitive;
    859   }
    860 
    861 
    862   #if defined(_WIN32) && !defined(UNDER_CE)
    863   NSecurity::EnablePrivilege_SymLink();
    864   #endif
    865 
    866   // options.LargePages = false;
    867 
    868   if (parser[NKey::kLargePages].ThereIs)
    869   {
    870     unsigned slp = 0;
    871     const UString &s = parser[NKey::kLargePages].PostStrings[0];
    872     if (s.IsEmpty())
    873       slp = 1;
    874     else if (s != L"-")
    875     {
    876       if (!StringToUInt32(s, slp))
    877         throw CArcCmdLineException("Unsupported switch postfix for -slp", s);
    878     }
    879 
    880     #ifdef _7ZIP_LARGE_PAGES
    881     if (slp >
    882           #ifndef UNDER_CE
    883             (unsigned)NSecurity::Get_LargePages_RiskLevel()
    884           #else
    885             0
    886           #endif
    887         )
    888     {
    889       SetLargePageSize();
    890       // note: this process also can inherit that Privilege from parent process
    891       g_LargePagesMode =
    892       #if defined(_WIN32) && !defined(UNDER_CE)
    893         NSecurity::EnablePrivilege_LockMemory();
    894       #else
    895         true;
    896       #endif
    897     }
    898     #endif
    899   }
    900 
    901 
    902   #ifndef UNDER_CE
    903 
    904   if (parser[NKey::kAffinity].ThereIs)
    905   {
    906     const UString &s = parser[NKey::kAffinity].PostStrings[0];
    907     if (!s.IsEmpty())
    908     {
    909       UInt32 v = 0;
    910       AString a;
    911       a.SetFromWStr_if_Ascii(s);
    912       if (!a.IsEmpty())
    913       {
    914         const char *end;
    915         v = ConvertHexStringToUInt32(a, &end);
    916         if (*end != 0)
    917           a.Empty();
    918       }
    919       if (a.IsEmpty())
    920         throw CArcCmdLineException("Unsupported switch postfix -stm", s);
    921 
    922       #ifdef _WIN32
    923       SetProcessAffinityMask(GetCurrentProcess(), v);
    924       #endif
    925     }
    926   }
    927 
    928   #endif
    929 }
    930 
    931 struct CCodePagePair
    932 {
    933   const char *Name;
    934   Int32 CodePage;
    935 };
    936 
    937 static const unsigned kNumByteOnlyCodePages = 3;
    938 
    939 static const CCodePagePair g_CodePagePairs[] =
    940 {
    941   { "utf-8", CP_UTF8 },
    942   { "win", CP_ACP },
    943   { "dos", CP_OEMCP },
    944   { "utf-16le", MY__CP_UTF16 },
    945   { "utf-16be", MY__CP_UTF16BE }
    946 };
    947 
    948 static Int32 FindCharset(const NCommandLineParser::CParser &parser, unsigned keyIndex,
    949     bool byteOnlyCodePages, Int32 defaultVal)
    950 {
    951   if (!parser[keyIndex].ThereIs)
    952     return defaultVal;
    953 
    954   UString name (parser[keyIndex].PostStrings.Back());
    955   UInt32 v;
    956   if (StringToUInt32(name, v))
    957     if (v < ((UInt32)1 << 16))
    958       return (Int32)v;
    959   name.MakeLower_Ascii();
    960   unsigned num = byteOnlyCodePages ? kNumByteOnlyCodePages : ARRAY_SIZE(g_CodePagePairs);
    961   for (unsigned i = 0;; i++)
    962   {
    963     if (i == num) // to disable warnings from different compilers
    964       throw CArcCmdLineException("Unsupported charset:", name);
    965     const CCodePagePair &pair = g_CodePagePairs[i];
    966     if (name.IsEqualTo(pair.Name))
    967       return pair.CodePage;
    968   }
    969 }
    970 
    971 
    972 static void SetBoolPair(NCommandLineParser::CParser &parser, unsigned switchID, CBoolPair &bp)
    973 {
    974   bp.Def = parser[switchID].ThereIs;
    975   if (bp.Def)
    976     bp.Val = !parser[switchID].WithMinus;
    977 }
    978 
    979 void CArcCmdLineParser::Parse2(CArcCmdLineOptions &options)
    980 {
    981   const UStringVector &nonSwitchStrings = parser.NonSwitchStrings;
    982   const unsigned numNonSwitchStrings = nonSwitchStrings.Size();
    983   if (numNonSwitchStrings < kMinNonSwitchWords)
    984     throw CArcCmdLineException("The command must be specified");
    985 
    986   if (!ParseArchiveCommand(nonSwitchStrings[kCommandIndex], options.Command))
    987     throw CArcCmdLineException("Unsupported command:", nonSwitchStrings[kCommandIndex]);
    988 
    989   if (parser[NKey::kHash].ThereIs)
    990     options.HashMethods = parser[NKey::kHash].PostStrings;
    991 
    992   if (parser[NKey::kElimDup].ThereIs)
    993   {
    994     options.ExtractOptions.ElimDup.Def = true;
    995     options.ExtractOptions.ElimDup.Val = !parser[NKey::kElimDup].WithMinus;
    996   }
    997 
    998   NWildcard::ECensorPathMode censorPathMode = NWildcard::k_RelatPath;
    999   bool fullPathMode = parser[NKey::kFullPathMode].ThereIs;
   1000   if (fullPathMode)
   1001   {
   1002     censorPathMode = NWildcard::k_AbsPath;
   1003     const UString &s = parser[NKey::kFullPathMode].PostStrings[0];
   1004     if (!s.IsEmpty())
   1005     {
   1006       if (s == L"2")
   1007         censorPathMode = NWildcard::k_FullPath;
   1008       else
   1009         throw CArcCmdLineException("Unsupported -spf:", s);
   1010     }
   1011   }
   1012 
   1013   if (parser[NKey::kNameTrailReplace].ThereIs)
   1014     g_PathTrailReplaceMode = !parser[NKey::kNameTrailReplace].WithMinus;
   1015 
   1016   NRecursedType::EEnum recursedType;
   1017   if (parser[NKey::kRecursed].ThereIs)
   1018     recursedType = GetRecursedTypeFromIndex(parser[NKey::kRecursed].PostCharIndex);
   1019   else
   1020     recursedType = NRecursedType::kNonRecursed;
   1021 
   1022   bool wildcardMatching = true;
   1023   if (parser[NKey::kDisableWildcardParsing].ThereIs)
   1024     wildcardMatching = false;
   1025 
   1026   g_CodePage = FindCharset(parser, NKey::kConsoleCharSet, true, -1);
   1027   Int32 codePage = FindCharset(parser, NKey::kListfileCharSet, false, CP_UTF8);
   1028 
   1029   bool thereAreSwitchIncludes = false;
   1030 
   1031   if (parser[NKey::kInclude].ThereIs)
   1032   {
   1033     thereAreSwitchIncludes = true;
   1034     AddSwitchWildcardsToCensor(options.Censor,
   1035         parser[NKey::kInclude].PostStrings, true, recursedType, wildcardMatching, codePage);
   1036   }
   1037 
   1038   if (parser[NKey::kExclude].ThereIs)
   1039     AddSwitchWildcardsToCensor(options.Censor,
   1040         parser[NKey::kExclude].PostStrings, false, recursedType, wildcardMatching, codePage);
   1041 
   1042   unsigned curCommandIndex = kCommandIndex + 1;
   1043   bool thereIsArchiveName = !parser[NKey::kNoArName].ThereIs &&
   1044       options.Command.CommandType != NCommandType::kBenchmark &&
   1045       options.Command.CommandType != NCommandType::kInfo &&
   1046       options.Command.CommandType != NCommandType::kHash;
   1047 
   1048   bool isExtractGroupCommand = options.Command.IsFromExtractGroup();
   1049   bool isExtractOrList = isExtractGroupCommand || options.Command.CommandType == NCommandType::kList;
   1050   bool isRename = options.Command.CommandType == NCommandType::kRename;
   1051 
   1052   if ((isExtractOrList || isRename) && options.StdInMode)
   1053     thereIsArchiveName = false;
   1054 
   1055   if (parser[NKey::kArcNameMode].ThereIs)
   1056     options.UpdateOptions.ArcNameMode = ParseArcNameMode(parser[NKey::kArcNameMode].PostCharIndex);
   1057 
   1058   if (thereIsArchiveName)
   1059   {
   1060     if (curCommandIndex >= numNonSwitchStrings)
   1061       throw CArcCmdLineException("Cannot find archive name");
   1062     options.ArchiveName = nonSwitchStrings[curCommandIndex++];
   1063     if (options.ArchiveName.IsEmpty())
   1064       throw CArcCmdLineException("Archive name cannot by empty");
   1065     #ifdef _WIN32
   1066     // options.ArchiveName.Replace(L'/', WCHAR_PATH_SEPARATOR);
   1067     #endif
   1068   }
   1069 
   1070   AddToCensorFromNonSwitchesStrings(isRename ? &options.UpdateOptions.RenamePairs : NULL,
   1071       curCommandIndex, options.Censor,
   1072       nonSwitchStrings, parser.StopSwitchIndex,
   1073       recursedType, wildcardMatching,
   1074       thereAreSwitchIncludes, codePage);
   1075 
   1076   options.YesToAll = parser[NKey::kYes].ThereIs;
   1077 
   1078 
   1079   #ifndef _NO_CRYPTO
   1080   options.PasswordEnabled = parser[NKey::kPassword].ThereIs;
   1081   if (options.PasswordEnabled)
   1082     options.Password = parser[NKey::kPassword].PostStrings[0];
   1083   #endif
   1084 
   1085   options.ShowDialog = parser[NKey::kShowDialog].ThereIs;
   1086 
   1087   if (parser[NKey::kArchiveType].ThereIs)
   1088     options.ArcType = parser[NKey::kArchiveType].PostStrings[0];
   1089 
   1090   options.ExcludedArcTypes = parser[NKey::kExcludedArcType].PostStrings;
   1091 
   1092   SetMethodOptions(parser, options.Properties);
   1093 
   1094   if (parser[NKey::kNtSecurity].ThereIs) options.NtSecurity.SetTrueTrue();
   1095 
   1096   SetBoolPair(parser, NKey::kAltStreams, options.AltStreams);
   1097   SetBoolPair(parser, NKey::kHardLinks, options.HardLinks);
   1098   SetBoolPair(parser, NKey::kSymLinks, options.SymLinks);
   1099 
   1100   if (isExtractOrList)
   1101   {
   1102     CExtractOptionsBase &eo = options.ExtractOptions;
   1103 
   1104     {
   1105       CExtractNtOptions &nt = eo.NtOptions;
   1106       nt.NtSecurity = options.NtSecurity;
   1107 
   1108       nt.AltStreams = options.AltStreams;
   1109       if (!options.AltStreams.Def)
   1110         nt.AltStreams.Val = true;
   1111 
   1112       nt.HardLinks = options.HardLinks;
   1113       if (!options.HardLinks.Def)
   1114         nt.HardLinks.Val = true;
   1115 
   1116       nt.SymLinks = options.SymLinks;
   1117       if (!options.SymLinks.Def)
   1118         nt.SymLinks.Val = true;
   1119 
   1120       nt.ReplaceColonForAltStream = parser[NKey::kReplaceColonForAltStream].ThereIs;
   1121       nt.WriteToAltStreamIfColon = parser[NKey::kWriteToAltStreamIfColon].ThereIs;
   1122     }
   1123 
   1124     options.Censor.AddPathsToCensor(NWildcard::k_AbsPath);
   1125     options.Censor.ExtendExclude();
   1126 
   1127     // are there paths that look as non-relative (!Prefix.IsEmpty())
   1128     if (!options.Censor.AllAreRelative())
   1129       throw CArcCmdLineException("Cannot use absolute pathnames for this command");
   1130 
   1131     NWildcard::CCensor &arcCensor = options.arcCensor;
   1132 
   1133     if (parser[NKey::kArInclude].ThereIs)
   1134       AddSwitchWildcardsToCensor(arcCensor, parser[NKey::kArInclude].PostStrings, true, NRecursedType::kNonRecursed, wildcardMatching, codePage);
   1135     if (parser[NKey::kArExclude].ThereIs)
   1136       AddSwitchWildcardsToCensor(arcCensor, parser[NKey::kArExclude].PostStrings, false, NRecursedType::kNonRecursed, wildcardMatching, codePage);
   1137 
   1138     if (thereIsArchiveName)
   1139       AddNameToCensor(arcCensor, options.ArchiveName, true, NRecursedType::kNonRecursed, wildcardMatching);
   1140 
   1141     arcCensor.AddPathsToCensor(NWildcard::k_RelatPath);
   1142 
   1143     #ifdef _WIN32
   1144     ConvertToLongNames(arcCensor);
   1145     #endif
   1146 
   1147     arcCensor.ExtendExclude();
   1148 
   1149     if (options.StdInMode)
   1150       options.ArcName_for_StdInMode = parser[NKey::kStdIn].PostStrings.Front();
   1151 
   1152     if (isExtractGroupCommand)
   1153     {
   1154       if (options.StdOutMode)
   1155       {
   1156         if (
   1157                   options.Number_for_Percents == k_OutStream_stdout
   1158             // || options.Number_for_Out      == k_OutStream_stdout
   1159             // || options.Number_for_Errors   == k_OutStream_stdout
   1160             ||
   1161             (
   1162               (options.IsStdOutTerminal && options.IsStdErrTerminal)
   1163               &&
   1164               (
   1165                       options.Number_for_Percents != k_OutStream_disabled
   1166                 // || options.Number_for_Out      != k_OutStream_disabled
   1167                 // || options.Number_for_Errors   != k_OutStream_disabled
   1168               )
   1169             )
   1170            )
   1171           throw CArcCmdLineException(kSameTerminalError);
   1172       }
   1173 
   1174       if (parser[NKey::kOutputDir].ThereIs)
   1175       {
   1176         eo.OutputDir = us2fs(parser[NKey::kOutputDir].PostStrings[0]);
   1177         NFile::NName::NormalizeDirPathPrefix(eo.OutputDir);
   1178       }
   1179 
   1180       eo.OverwriteMode = NExtract::NOverwriteMode::kAsk;
   1181       if (parser[NKey::kOverwrite].ThereIs)
   1182       {
   1183         eo.OverwriteMode = k_OverwriteModes[(unsigned)parser[NKey::kOverwrite].PostCharIndex];
   1184         eo.OverwriteMode_Force = true;
   1185       }
   1186       else if (options.YesToAll)
   1187       {
   1188         eo.OverwriteMode = NExtract::NOverwriteMode::kOverwrite;
   1189         eo.OverwriteMode_Force = true;
   1190       }
   1191     }
   1192 
   1193     eo.PathMode = options.Command.GetPathMode();
   1194     if (censorPathMode == NWildcard::k_AbsPath)
   1195     {
   1196       eo.PathMode = NExtract::NPathMode::kAbsPaths;
   1197       eo.PathMode_Force = true;
   1198     }
   1199     else if (censorPathMode == NWildcard::k_FullPath)
   1200     {
   1201       eo.PathMode = NExtract::NPathMode::kFullPaths;
   1202       eo.PathMode_Force = true;
   1203     }
   1204   }
   1205   else if (options.Command.IsFromUpdateGroup())
   1206   {
   1207     if (parser[NKey::kArInclude].ThereIs)
   1208       throw CArcCmdLineException("-ai switch is not supported for this command");
   1209 
   1210     CUpdateOptions &updateOptions = options.UpdateOptions;
   1211 
   1212     SetAddCommandOptions(options.Command.CommandType, parser, updateOptions);
   1213 
   1214     updateOptions.MethodMode.Properties = options.Properties;
   1215 
   1216     if (parser[NKey::kShareForWrite].ThereIs)
   1217       updateOptions.OpenShareForWrite = true;
   1218     if (parser[NKey::kStopAfterOpenError].ThereIs)
   1219       updateOptions.StopAfterOpenError = true;
   1220 
   1221     updateOptions.PathMode = censorPathMode;
   1222 
   1223     updateOptions.AltStreams = options.AltStreams;
   1224     updateOptions.NtSecurity = options.NtSecurity;
   1225     updateOptions.HardLinks = options.HardLinks;
   1226     updateOptions.SymLinks = options.SymLinks;
   1227 
   1228     updateOptions.EMailMode = parser[NKey::kEmail].ThereIs;
   1229     if (updateOptions.EMailMode)
   1230     {
   1231       updateOptions.EMailAddress = parser[NKey::kEmail].PostStrings.Front();
   1232       if (updateOptions.EMailAddress.Len() > 0)
   1233         if (updateOptions.EMailAddress[0] == L'.')
   1234         {
   1235           updateOptions.EMailRemoveAfter = true;
   1236           updateOptions.EMailAddress.Delete(0);
   1237         }
   1238     }
   1239 
   1240     updateOptions.StdOutMode = options.StdOutMode;
   1241     updateOptions.StdInMode = options.StdInMode;
   1242 
   1243     updateOptions.DeleteAfterCompressing = parser[NKey::kDeleteAfterCompressing].ThereIs;
   1244     updateOptions.SetArcMTime = parser[NKey::kSetArcMTime].ThereIs;
   1245 
   1246     if (updateOptions.StdOutMode && updateOptions.EMailMode)
   1247       throw CArcCmdLineException("stdout mode and email mode cannot be combined");
   1248 
   1249     if (updateOptions.StdOutMode)
   1250     {
   1251       if (options.IsStdOutTerminal)
   1252         throw CArcCmdLineException(kTerminalOutError);
   1253 
   1254       if (options.Number_for_Percents == k_OutStream_stdout
   1255           || options.Number_for_Out == k_OutStream_stdout
   1256           || options.Number_for_Errors == k_OutStream_stdout)
   1257         throw CArcCmdLineException(kSameTerminalError);
   1258     }
   1259 
   1260     if (updateOptions.StdInMode)
   1261       updateOptions.StdInFileName = parser[NKey::kStdIn].PostStrings.Front();
   1262 
   1263     if (options.Command.CommandType == NCommandType::kRename)
   1264       if (updateOptions.Commands.Size() != 1)
   1265         throw CArcCmdLineException("Only one archive can be created with rename command");
   1266   }
   1267   else if (options.Command.CommandType == NCommandType::kBenchmark)
   1268   {
   1269     options.NumIterations = 1;
   1270     if (curCommandIndex < numNonSwitchStrings)
   1271     {
   1272       if (!StringToUInt32(nonSwitchStrings[curCommandIndex], options.NumIterations))
   1273         throw CArcCmdLineException("Incorrect Number of benmchmark iterations", nonSwitchStrings[curCommandIndex]);
   1274       curCommandIndex++;
   1275     }
   1276   }
   1277   else if (options.Command.CommandType == NCommandType::kHash)
   1278   {
   1279     options.Censor.AddPathsToCensor(censorPathMode);
   1280     options.Censor.ExtendExclude();
   1281 
   1282     CHashOptions &hashOptions = options.HashOptions;
   1283     hashOptions.PathMode = censorPathMode;
   1284     hashOptions.Methods = options.HashMethods;
   1285     if (parser[NKey::kShareForWrite].ThereIs)
   1286       hashOptions.OpenShareForWrite = true;
   1287     hashOptions.StdInMode = options.StdInMode;
   1288     hashOptions.AltStreamsMode = options.AltStreams.Val;
   1289   }
   1290   else if (options.Command.CommandType == NCommandType::kInfo)
   1291   {
   1292   }
   1293   else
   1294     throw 20150919;
   1295 }
   1296