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