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