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