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