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