• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
StringToUInt32(const wchar_t * s,UInt32 & v)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 
CArcCmdLineException(const char * a,const wchar_t * u)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 
ParseArcNameMode(int postCharIndex)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 
IsFromExtractGroup() const278 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 
GetPathMode() const290 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 
IsFromUpdateGroup() const301 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 
GetRecursedTypeFromIndex(int index)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 
ParseArchiveCommand(const UString & commandString,CArcCommand & command)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 
AddNameToCensor(NWildcard::CCensor & censor,const UString & name,bool include,NRecursedType::EEnum type,bool wildcardMatching)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 
AddRenamePair(CObjectVector<CRenamePair> * renamePairs,const UString & oldName,const UString & newName,NRecursedType::EEnum type,bool wildcardMatching)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 
AddToCensorFromListFile(CObjectVector<CRenamePair> * renamePairs,NWildcard::CCensor & censor,LPCWSTR fileName,bool include,NRecursedType::EEnum type,bool wildcardMatching,Int32 codePage)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 
AddToCensorFromNonSwitchesStrings(CObjectVector<CRenamePair> * renamePairs,unsigned startIndex,NWildcard::CCensor & censor,const UStringVector & nonSwitchStrings,NRecursedType::EEnum type,bool wildcardMatching,bool thereAreSwitchIncludes,Int32 codePage)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 
CEventSetEndCEventSetEnd471   CEventSetEnd(const wchar_t *name): Name(name) {}
~CEventSetEndCEventSetEnd472   ~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 
ParseMapWithPaths(NWildcard::CCensor & censor,const UString & s2,bool include,NRecursedType::EEnum commonRecursedType,bool wildcardMatching)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 
AddSwitchWildcardsToCensor(NWildcard::CCensor & censor,const UStringVector & strings,bool include,NRecursedType::EEnum commonRecursedType,bool wildcardMatching,Int32 codePage)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 
ConvertToLongName(const UString & prefix,UString & name)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 
ConvertToLongNames(const UString & prefix,CObjectVector<NWildcard::CItem> & items)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 
ConvertToLongNames(const UString & prefix,NWildcard::CCensorNode & node)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 
ConvertToLongNames(NWildcard::CCensor & censor)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 
ParseUpdateCommandString2(const UString & command,NUpdateArchive::CActionSet & actionSet,UString & postString)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 
ParseUpdateCommandString(CUpdateOptions & options,const UStringVector & updatePostStrings,const NUpdateArchive::CActionSet & defaultActionSet)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 
SetAddCommandOptions(NCommandType::EEnum commandType,const CParser & parser,CUpdateOptions & options)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 
SetMethodOptions(const CParser & parser,CObjectVector<CProperty> & properties)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 
CArcCmdLineParser()852 CArcCmdLineParser::CArcCmdLineParser(): parser(ARRAY_SIZE(kSwitchForms)) {}
853 
SetStreamMode(const CSwitchResult & sw,unsigned & res)854 static inline void SetStreamMode(const CSwitchResult &sw, unsigned &res)
855 {
856   if (sw.ThereIs)
857     res = sw.PostCharIndex;
858 }
859 
Parse1(const UStringVector & commandStrings,CArcCmdLineOptions & options)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 
FindCharset(const NCommandLineParser::CParser & parser,unsigned keyIndex,bool byteOnlyCodePages,Int32 defaultVal)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 
EnumerateDirItemsAndSort(NWildcard::CCensor & censor,NWildcard::ECensorPathMode censorPathMode,const UString & addPathPrefix,UStringVector & sortedPaths,UStringVector & sortedFullPaths,CDirItemsStat & st,IDirItemsCallback * callback)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 
SetBoolPair(NCommandLineParser::CParser & parser,unsigned switchID,CBoolPair & bp)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 
Parse2(CArcCmdLineOptions & options)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