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/FileDir.h"
27 #include "../../../Windows/FileName.h"
28 #ifdef _WIN32
29 #include "../../../Windows/FileMapping.h"
30 #include "../../../Windows/MemoryLock.h"
31 #include "../../../Windows/Synchronization.h"
32 #endif
33 
34 #include "ArchiveCommandLine.h"
35 #include "EnumDirItems.h"
36 #include "Update.h"
37 #include "UpdateAction.h"
38 
39 extern bool g_CaseSensitive;
40 extern bool g_PathTrailReplaceMode;
41 
42 bool g_LargePagesMode = false;
43 
44 #ifdef UNDER_CE
45 
46 #define MY_IS_TERMINAL(x) false;
47 
48 #else
49 
50 #if _MSC_VER >= 1400
51 #define MY_isatty_fileno(x) _isatty(_fileno(x))
52 #else
53 #define MY_isatty_fileno(x) isatty(fileno(x))
54 #endif
55 
56 #define MY_IS_TERMINAL(x) (MY_isatty_fileno(x) != 0);
57 
58 #endif
59 
60 using namespace NCommandLineParser;
61 using namespace NWindows;
62 using namespace NFile;
63 
StringToUInt32(const wchar_t * s,UInt32 & v)64 static bool StringToUInt32(const wchar_t *s, UInt32 &v)
65 {
66   if (*s == 0)
67     return false;
68   const wchar_t *end;
69   v = ConvertStringToUInt32(s, &end);
70   return *end == 0;
71 }
72 
73 
74 int g_CodePage = -1;
75 
76 namespace NKey {
77 enum Enum
78 {
79   kHelp1 = 0,
80   kHelp2,
81   kHelp3,
82 
83   kDisableHeaders,
84   kDisablePercents,
85   kShowTime,
86   kLogLevel,
87 
88   kOutStream,
89   kErrStream,
90   kPercentStream,
91 
92   kYes,
93 
94   kShowDialog,
95   kOverwrite,
96 
97   kArchiveType,
98   kExcludedArcType,
99 
100   kProperty,
101   kOutputDir,
102   kWorkingDir,
103 
104   kInclude,
105   kExclude,
106   kArInclude,
107   kArExclude,
108   kNoArName,
109 
110   kUpdate,
111   kVolume,
112   kRecursed,
113 
114   kAffinity,
115   kSfx,
116   kEmail,
117   kHash,
118 
119   kStdIn,
120   kStdOut,
121 
122   kLargePages,
123   kListfileCharSet,
124   kConsoleCharSet,
125   kTechMode,
126 
127   kShareForWrite,
128   kStopAfterOpenError,
129   kCaseSensitive,
130   kArcNameMode,
131 
132   kDisableWildcardParsing,
133   kElimDup,
134   kFullPathMode,
135 
136   kHardLinks,
137   kSymLinks,
138   kNtSecurity,
139 
140   kAltStreams,
141   kReplaceColonForAltStream,
142   kWriteToAltStreamIfColon,
143 
144   kNameTrailReplace,
145 
146   kDeleteAfterCompressing,
147   kSetArcMTime
148 
149   #ifndef _NO_CRYPTO
150   , kPassword
151   #endif
152 };
153 
154 }
155 
156 
157 static const wchar_t kRecursedIDChar = 'r';
158 static const char * const kRecursedPostCharSet = "0-";
159 
160 static const char * const k_ArcNameMode_PostCharSet = "sea";
161 
162 static const char * const k_Stream_PostCharSet = "012";
163 
ParseArcNameMode(int postCharIndex)164 static inline const EArcNameMode ParseArcNameMode(int postCharIndex)
165 {
166   switch (postCharIndex)
167   {
168     case 1: return k_ArcNameMode_Exact;
169     case 2: return k_ArcNameMode_Add;
170     default: return k_ArcNameMode_Smart;
171   }
172 }
173 
174 namespace NRecursedPostCharIndex {
175   enum EEnum
176   {
177     kWildcardRecursionOnly = 0,
178     kNoRecursion = 1
179   };
180 }
181 
182 static const char kImmediateNameID = '!';
183 static const char kMapNameID = '#';
184 static const char kFileListID = '@';
185 
186 static const char kSomeCludePostStringMinSize = 2; // at least <@|!><N>ame must be
187 static const char kSomeCludeAfterRecursedPostStringMinSize = 2; // at least <@|!><N>ame must be
188 
189 static const char * const kOverwritePostCharSet = "asut";
190 
191 static const NExtract::NOverwriteMode::EEnum k_OverwriteModes[] =
192 {
193   NExtract::NOverwriteMode::kOverwrite,
194   NExtract::NOverwriteMode::kSkip,
195   NExtract::NOverwriteMode::kRename,
196   NExtract::NOverwriteMode::kRenameExisting
197 };
198 
199 static const CSwitchForm kSwitchForms[] =
200 {
201   { "?" },
202   { "h" },
203   { "-help" },
204 
205   { "ba" },
206   { "bd" },
207   { "bt" },
208   { "bb", NSwitchType::kString, false, 0 },
209 
210   { "bso", NSwitchType::kChar, false, 1, k_Stream_PostCharSet },
211   { "bse", NSwitchType::kChar, false, 1, k_Stream_PostCharSet },
212   { "bsp", NSwitchType::kChar, false, 1, k_Stream_PostCharSet },
213 
214   { "y" },
215 
216   { "ad" },
217   { "ao", NSwitchType::kChar, false, 1, kOverwritePostCharSet},
218 
219   { "t",  NSwitchType::kString, false, 1 },
220   { "stx", NSwitchType::kString, true, 1 },
221 
222   { "m",  NSwitchType::kString, true, 1 },
223   { "o",  NSwitchType::kString, false, 1 },
224   { "w",  NSwitchType::kString },
225 
226   { "i",  NSwitchType::kString, true, kSomeCludePostStringMinSize},
227   { "x",  NSwitchType::kString, true, kSomeCludePostStringMinSize},
228   { "ai", NSwitchType::kString, true, kSomeCludePostStringMinSize},
229   { "ax", NSwitchType::kString, true, kSomeCludePostStringMinSize},
230   { "an" },
231 
232   { "u",  NSwitchType::kString, true, 1},
233   { "v",  NSwitchType::kString, true, 1},
234   { "r",  NSwitchType::kChar, false, 0, kRecursedPostCharSet },
235 
236   { "stm", NSwitchType::kString },
237   { "sfx", NSwitchType::kString },
238   { "seml", NSwitchType::kString, false, 0},
239   { "scrc", NSwitchType::kString, true, 0 },
240 
241   { "si", NSwitchType::kString },
242   { "so" },
243 
244   { "slp", NSwitchType::kString },
245   { "scs", NSwitchType::kString },
246   { "scc", NSwitchType::kString },
247   { "slt" },
248 
249   { "ssw" },
250   { "sse" },
251   { "ssc", NSwitchType::kMinus },
252   { "sa",  NSwitchType::kChar, false, 1, k_ArcNameMode_PostCharSet },
253 
254   { "spd" },
255   { "spe", NSwitchType::kMinus },
256   { "spf", NSwitchType::kString, false, 0 },
257 
258   { "snh", NSwitchType::kMinus },
259   { "snl", NSwitchType::kMinus },
260   { "sni" },
261 
262   { "sns", NSwitchType::kMinus },
263   { "snr" },
264   { "snc" },
265 
266   { "snt", NSwitchType::kMinus },
267 
268   { "sdel" },
269   { "stl" }
270 
271   #ifndef _NO_CRYPTO
272   , { "p",  NSwitchType::kString }
273   #endif
274 };
275 
276 static const char * const kUniversalWildcard = "*";
277 static const unsigned kMinNonSwitchWords = 1;
278 static const unsigned kCommandIndex = 0;
279 
280 // static const char * const kUserErrorMessage  = "Incorrect command line";
281 static const char * const kCannotFindListFile = "Cannot find listfile";
282 static const char * const kIncorrectListFile = "Incorrect item in listfile.\nCheck charset encoding and -scs switch.";
283 static const char * const kTerminalOutError = "I won't write compressed data to a terminal";
284 static const char * const kSameTerminalError = "I won't write data and program's messages to same stream";
285 static const char * const kEmptyFilePath = "Empty file path";
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 /*
619 static NUpdateArchive::NPairAction::EEnum GetUpdatePairActionType(int i)
620 {
621   switch (i)
622   {
623     case NUpdateArchive::NPairAction::kIgnore: return NUpdateArchive::NPairAction::kIgnore;
624     case NUpdateArchive::NPairAction::kCopy: return NUpdateArchive::NPairAction::kCopy;
625     case NUpdateArchive::NPairAction::kCompress: return NUpdateArchive::NPairAction::kCompress;
626     case NUpdateArchive::NPairAction::kCompressAsAnti: return NUpdateArchive::NPairAction::kCompressAsAnti;
627   }
628   throw 98111603;
629 }
630 */
631 
632 static const char * const kUpdatePairStateIDSet = "pqrxyzw";
633 static const int kUpdatePairStateNotSupportedActions[] = {2, 2, 1, -1, -1, -1, -1};
634 
635 static const unsigned kNumUpdatePairActions = 4;
636 static const char * const kUpdateIgnoreItselfPostStringID = "-";
637 static const wchar_t kUpdateNewArchivePostCharID = '!';
638 
639 
ParseUpdateCommandString2(const UString & command,NUpdateArchive::CActionSet & actionSet,UString & postString)640 static bool ParseUpdateCommandString2(const UString &command,
641     NUpdateArchive::CActionSet &actionSet, UString &postString)
642 {
643   for (unsigned i = 0; i < command.Len();)
644   {
645     wchar_t c = MyCharLower_Ascii(command[i]);
646     int statePos = FindCharPosInString(kUpdatePairStateIDSet, (char)c);
647     if (c > 0x7F || statePos < 0)
648     {
649       postString = command.Ptr(i);
650       return true;
651     }
652     i++;
653     if (i >= command.Len())
654       return false;
655     c = command[i];
656     if (c < '0' || c >= '0' + kNumUpdatePairActions)
657       return false;
658     unsigned actionPos = c - '0';
659     actionSet.StateActions[(unsigned)statePos] = (NUpdateArchive::NPairAction::EEnum)(actionPos);
660     if (kUpdatePairStateNotSupportedActions[(unsigned)statePos] == (int)actionPos)
661       return false;
662     i++;
663   }
664   postString.Empty();
665   return true;
666 }
667 
ParseUpdateCommandString(CUpdateOptions & options,const UStringVector & updatePostStrings,const NUpdateArchive::CActionSet & defaultActionSet)668 static void ParseUpdateCommandString(CUpdateOptions &options,
669     const UStringVector &updatePostStrings,
670     const NUpdateArchive::CActionSet &defaultActionSet)
671 {
672   const char *errorMessage = "incorrect update switch command";
673   unsigned i;
674   for (i = 0; i < updatePostStrings.Size(); i++)
675   {
676     const UString &updateString = updatePostStrings[i];
677     if (updateString.IsEqualTo(kUpdateIgnoreItselfPostStringID))
678     {
679       if (options.UpdateArchiveItself)
680       {
681         options.UpdateArchiveItself = false;
682         options.Commands.Delete(0);
683       }
684     }
685     else
686     {
687       NUpdateArchive::CActionSet actionSet = defaultActionSet;
688 
689       UString postString;
690       if (!ParseUpdateCommandString2(updateString, actionSet, postString))
691         break;
692       if (postString.IsEmpty())
693       {
694         if (options.UpdateArchiveItself)
695           options.Commands[0].ActionSet = actionSet;
696       }
697       else
698       {
699         if (postString[0] != kUpdateNewArchivePostCharID)
700           break;
701         CUpdateArchiveCommand uc;
702         UString archivePath = postString.Ptr(1);
703         if (archivePath.IsEmpty())
704           break;
705         uc.UserArchivePath = archivePath;
706         uc.ActionSet = actionSet;
707         options.Commands.Add(uc);
708       }
709     }
710   }
711   if (i != updatePostStrings.Size())
712     throw CArcCmdLineException(errorMessage, updatePostStrings[i]);
713 }
714 
715 bool ParseComplexSize(const wchar_t *s, UInt64 &result);
716 
SetAddCommandOptions(NCommandType::EEnum commandType,const CParser & parser,CUpdateOptions & options)717 static void SetAddCommandOptions(
718     NCommandType::EEnum commandType,
719     const CParser &parser,
720     CUpdateOptions &options)
721 {
722   NUpdateArchive::CActionSet defaultActionSet;
723   switch (commandType)
724   {
725     case NCommandType::kAdd:
726       defaultActionSet = NUpdateArchive::k_ActionSet_Add;
727       break;
728     case NCommandType::kDelete:
729       defaultActionSet = NUpdateArchive::k_ActionSet_Delete;
730       break;
731     default:
732       defaultActionSet = NUpdateArchive::k_ActionSet_Update;
733   }
734 
735   options.UpdateArchiveItself = true;
736 
737   options.Commands.Clear();
738   CUpdateArchiveCommand updateMainCommand;
739   updateMainCommand.ActionSet = defaultActionSet;
740   options.Commands.Add(updateMainCommand);
741   if (parser[NKey::kUpdate].ThereIs)
742     ParseUpdateCommandString(options, parser[NKey::kUpdate].PostStrings,
743         defaultActionSet);
744   if (parser[NKey::kWorkingDir].ThereIs)
745   {
746     const UString &postString = parser[NKey::kWorkingDir].PostStrings[0];
747     if (postString.IsEmpty())
748       NDir::MyGetTempPath(options.WorkingDir);
749     else
750       options.WorkingDir = us2fs(postString);
751   }
752   options.SfxMode = parser[NKey::kSfx].ThereIs;
753   if (options.SfxMode)
754     options.SfxModule = us2fs(parser[NKey::kSfx].PostStrings[0]);
755 
756   if (parser[NKey::kVolume].ThereIs)
757   {
758     const UStringVector &sv = parser[NKey::kVolume].PostStrings;
759     FOR_VECTOR (i, sv)
760     {
761       UInt64 size;
762       if (!ParseComplexSize(sv[i], size) || size == 0)
763         throw CArcCmdLineException("Incorrect volume size:", sv[i]);
764       options.VolumesSizes.Add(size);
765     }
766   }
767 }
768 
SetMethodOptions(const CParser & parser,CObjectVector<CProperty> & properties)769 static void SetMethodOptions(const CParser &parser, CObjectVector<CProperty> &properties)
770 {
771   if (parser[NKey::kProperty].ThereIs)
772   {
773     FOR_VECTOR (i, parser[NKey::kProperty].PostStrings)
774     {
775       CProperty prop;
776       prop.Name = parser[NKey::kProperty].PostStrings[i];
777       int index = prop.Name.Find(L'=');
778       if (index >= 0)
779       {
780         prop.Value = prop.Name.Ptr(index + 1);
781         prop.Name.DeleteFrom(index);
782       }
783       properties.Add(prop);
784     }
785   }
786 }
787 
788 
SetStreamMode(const CSwitchResult & sw,unsigned & res)789 static inline void SetStreamMode(const CSwitchResult &sw, unsigned &res)
790 {
791   if (sw.ThereIs)
792     res = sw.PostCharIndex;
793 }
794 
795 
Parse1(const UStringVector & commandStrings,CArcCmdLineOptions & options)796 void CArcCmdLineParser::Parse1(const UStringVector &commandStrings,
797     CArcCmdLineOptions &options)
798 {
799   if (!parser.ParseStrings(kSwitchForms, ARRAY_SIZE(kSwitchForms), commandStrings))
800     throw CArcCmdLineException(parser.ErrorMessage, parser.ErrorLine);
801 
802   options.IsInTerminal = MY_IS_TERMINAL(stdin);
803   options.IsStdOutTerminal = MY_IS_TERMINAL(stdout);
804   options.IsStdErrTerminal = MY_IS_TERMINAL(stderr);
805 
806   options.HelpMode = parser[NKey::kHelp1].ThereIs || parser[NKey::kHelp2].ThereIs  || parser[NKey::kHelp3].ThereIs;
807 
808   options.StdInMode = parser[NKey::kStdIn].ThereIs;
809   options.StdOutMode = parser[NKey::kStdOut].ThereIs;
810   options.EnableHeaders = !parser[NKey::kDisableHeaders].ThereIs;
811   options.TechMode = parser[NKey::kTechMode].ThereIs;
812   options.ShowTime = parser[NKey::kShowTime].ThereIs;
813 
814   if (parser[NKey::kDisablePercents].ThereIs
815       || options.StdOutMode
816       || !options.IsStdOutTerminal)
817     options.Number_for_Percents = k_OutStream_disabled;
818 
819   if (options.StdOutMode)
820     options.Number_for_Out = k_OutStream_disabled;
821 
822   SetStreamMode(parser[NKey::kOutStream], options.Number_for_Out);
823   SetStreamMode(parser[NKey::kErrStream], options.Number_for_Errors);
824   SetStreamMode(parser[NKey::kPercentStream], options.Number_for_Percents);
825 
826   if (parser[NKey::kLogLevel].ThereIs)
827   {
828     const UString &s = parser[NKey::kLogLevel].PostStrings[0];
829     if (s.IsEmpty())
830       options.LogLevel = 1;
831     else
832     {
833       UInt32 v;
834       if (!StringToUInt32(s, v))
835         throw CArcCmdLineException("Unsupported switch postfix -bb", s);
836       options.LogLevel = (unsigned)v;
837     }
838   }
839 
840   if (parser[NKey::kCaseSensitive].ThereIs)
841   {
842     g_CaseSensitive = !parser[NKey::kCaseSensitive].WithMinus;
843     options.CaseSensitiveChange = true;
844     options.CaseSensitive = g_CaseSensitive;
845   }
846 
847 
848   #if defined(_WIN32) && !defined(UNDER_CE)
849   NSecurity::EnablePrivilege_SymLink();
850   #endif
851 
852   // options.LargePages = false;
853 
854   if (parser[NKey::kLargePages].ThereIs)
855   {
856     unsigned slp = 0;
857     const UString &s = parser[NKey::kLargePages].PostStrings[0];
858     if (s.IsEmpty())
859       slp = 1;
860     else if (s != L"-")
861     {
862       if (!StringToUInt32(s, slp))
863         throw CArcCmdLineException("Unsupported switch postfix for -slp", s);
864     }
865 
866     #ifdef _7ZIP_LARGE_PAGES
867     if (slp >
868           #ifndef UNDER_CE
869             (unsigned)NSecurity::Get_LargePages_RiskLevel()
870           #else
871             0
872           #endif
873         )
874     {
875       SetLargePageSize();
876       // note: this process also can inherit that Privilege from parent process
877       g_LargePagesMode =
878       #if defined(_WIN32) && !defined(UNDER_CE)
879         NSecurity::EnablePrivilege_LockMemory();
880       #else
881         true;
882       #endif
883     }
884     #endif
885   }
886 
887 
888   #ifndef UNDER_CE
889 
890   if (parser[NKey::kAffinity].ThereIs)
891   {
892     const UString &s = parser[NKey::kAffinity].PostStrings[0];
893     if (!s.IsEmpty())
894     {
895       UInt32 v = 0;
896       AString a;
897       a.SetFromWStr_if_Ascii(s);
898       if (!a.IsEmpty())
899       {
900         const char *end;
901         v = ConvertHexStringToUInt32(a, &end);
902         if (*end != 0)
903           a.Empty();
904       }
905       if (a.IsEmpty())
906         throw CArcCmdLineException("Unsupported switch postfix -stm", s);
907 
908       #ifdef _WIN32
909       SetProcessAffinityMask(GetCurrentProcess(), v);
910       #endif
911     }
912   }
913 
914   #endif
915 }
916 
917 struct CCodePagePair
918 {
919   const char *Name;
920   Int32 CodePage;
921 };
922 
923 static const unsigned kNumByteOnlyCodePages = 3;
924 
925 static const CCodePagePair g_CodePagePairs[] =
926 {
927   { "utf-8", CP_UTF8 },
928   { "win", CP_ACP },
929   { "dos", CP_OEMCP },
930   { "utf-16le", MY__CP_UTF16 },
931   { "utf-16be", MY__CP_UTF16BE }
932 };
933 
FindCharset(const NCommandLineParser::CParser & parser,unsigned keyIndex,bool byteOnlyCodePages,Int32 defaultVal)934 static Int32 FindCharset(const NCommandLineParser::CParser &parser, unsigned keyIndex,
935     bool byteOnlyCodePages, Int32 defaultVal)
936 {
937   if (!parser[keyIndex].ThereIs)
938     return defaultVal;
939 
940   UString name (parser[keyIndex].PostStrings.Back());
941   UInt32 v;
942   if (StringToUInt32(name, v))
943     if (v < ((UInt32)1 << 16))
944       return (Int32)v;
945   name.MakeLower_Ascii();
946   unsigned num = byteOnlyCodePages ? kNumByteOnlyCodePages : ARRAY_SIZE(g_CodePagePairs);
947   for (unsigned i = 0;; i++)
948   {
949     if (i == num) // to disable warnings from different compilers
950       throw CArcCmdLineException("Unsupported charset:", name);
951     const CCodePagePair &pair = g_CodePagePairs[i];
952     if (name.IsEqualTo(pair.Name))
953       return pair.CodePage;
954   }
955 }
956 
957 
SetBoolPair(NCommandLineParser::CParser & parser,unsigned switchID,CBoolPair & bp)958 static void SetBoolPair(NCommandLineParser::CParser &parser, unsigned switchID, CBoolPair &bp)
959 {
960   bp.Def = parser[switchID].ThereIs;
961   if (bp.Def)
962     bp.Val = !parser[switchID].WithMinus;
963 }
964 
Parse2(CArcCmdLineOptions & options)965 void CArcCmdLineParser::Parse2(CArcCmdLineOptions &options)
966 {
967   const UStringVector &nonSwitchStrings = parser.NonSwitchStrings;
968   const unsigned numNonSwitchStrings = nonSwitchStrings.Size();
969   if (numNonSwitchStrings < kMinNonSwitchWords)
970     throw CArcCmdLineException("The command must be specified");
971 
972   if (!ParseArchiveCommand(nonSwitchStrings[kCommandIndex], options.Command))
973     throw CArcCmdLineException("Unsupported command:", nonSwitchStrings[kCommandIndex]);
974 
975   if (parser[NKey::kHash].ThereIs)
976     options.HashMethods = parser[NKey::kHash].PostStrings;
977 
978   if (parser[NKey::kElimDup].ThereIs)
979   {
980     options.ExtractOptions.ElimDup.Def = true;
981     options.ExtractOptions.ElimDup.Val = !parser[NKey::kElimDup].WithMinus;
982   }
983 
984   NWildcard::ECensorPathMode censorPathMode = NWildcard::k_RelatPath;
985   bool fullPathMode = parser[NKey::kFullPathMode].ThereIs;
986   if (fullPathMode)
987   {
988     censorPathMode = NWildcard::k_AbsPath;
989     const UString &s = parser[NKey::kFullPathMode].PostStrings[0];
990     if (!s.IsEmpty())
991     {
992       if (s == L"2")
993         censorPathMode = NWildcard::k_FullPath;
994       else
995         throw CArcCmdLineException("Unsupported -spf:", s);
996     }
997   }
998 
999   if (parser[NKey::kNameTrailReplace].ThereIs)
1000     g_PathTrailReplaceMode = !parser[NKey::kNameTrailReplace].WithMinus;
1001 
1002   NRecursedType::EEnum recursedType;
1003   if (parser[NKey::kRecursed].ThereIs)
1004     recursedType = GetRecursedTypeFromIndex(parser[NKey::kRecursed].PostCharIndex);
1005   else
1006     recursedType = NRecursedType::kNonRecursed;
1007 
1008   bool wildcardMatching = true;
1009   if (parser[NKey::kDisableWildcardParsing].ThereIs)
1010     wildcardMatching = false;
1011 
1012   g_CodePage = FindCharset(parser, NKey::kConsoleCharSet, true, -1);
1013   Int32 codePage = FindCharset(parser, NKey::kListfileCharSet, false, CP_UTF8);
1014 
1015   bool thereAreSwitchIncludes = false;
1016 
1017   if (parser[NKey::kInclude].ThereIs)
1018   {
1019     thereAreSwitchIncludes = true;
1020     AddSwitchWildcardsToCensor(options.Censor,
1021         parser[NKey::kInclude].PostStrings, true, recursedType, wildcardMatching, codePage);
1022   }
1023 
1024   if (parser[NKey::kExclude].ThereIs)
1025     AddSwitchWildcardsToCensor(options.Censor,
1026         parser[NKey::kExclude].PostStrings, false, recursedType, wildcardMatching, codePage);
1027 
1028   unsigned curCommandIndex = kCommandIndex + 1;
1029   bool thereIsArchiveName = !parser[NKey::kNoArName].ThereIs &&
1030       options.Command.CommandType != NCommandType::kBenchmark &&
1031       options.Command.CommandType != NCommandType::kInfo &&
1032       options.Command.CommandType != NCommandType::kHash;
1033 
1034   bool isExtractGroupCommand = options.Command.IsFromExtractGroup();
1035   bool isExtractOrList = isExtractGroupCommand || options.Command.CommandType == NCommandType::kList;
1036   bool isRename = options.Command.CommandType == NCommandType::kRename;
1037 
1038   if ((isExtractOrList || isRename) && options.StdInMode)
1039     thereIsArchiveName = false;
1040 
1041   if (parser[NKey::kArcNameMode].ThereIs)
1042     options.UpdateOptions.ArcNameMode = ParseArcNameMode(parser[NKey::kArcNameMode].PostCharIndex);
1043 
1044   if (thereIsArchiveName)
1045   {
1046     if (curCommandIndex >= numNonSwitchStrings)
1047       throw CArcCmdLineException("Cannot find archive name");
1048     options.ArchiveName = nonSwitchStrings[curCommandIndex++];
1049     if (options.ArchiveName.IsEmpty())
1050       throw CArcCmdLineException("Archive name cannot by empty");
1051     #ifdef _WIN32
1052     // options.ArchiveName.Replace(L'/', WCHAR_PATH_SEPARATOR);
1053     #endif
1054   }
1055 
1056   AddToCensorFromNonSwitchesStrings(isRename ? &options.UpdateOptions.RenamePairs : NULL,
1057       curCommandIndex, options.Censor,
1058       nonSwitchStrings, parser.StopSwitchIndex,
1059       recursedType, wildcardMatching,
1060       thereAreSwitchIncludes, codePage);
1061 
1062   options.YesToAll = parser[NKey::kYes].ThereIs;
1063 
1064 
1065   #ifndef _NO_CRYPTO
1066   options.PasswordEnabled = parser[NKey::kPassword].ThereIs;
1067   if (options.PasswordEnabled)
1068     options.Password = parser[NKey::kPassword].PostStrings[0];
1069   #endif
1070 
1071   options.ShowDialog = parser[NKey::kShowDialog].ThereIs;
1072 
1073   if (parser[NKey::kArchiveType].ThereIs)
1074     options.ArcType = parser[NKey::kArchiveType].PostStrings[0];
1075 
1076   options.ExcludedArcTypes = parser[NKey::kExcludedArcType].PostStrings;
1077 
1078   SetMethodOptions(parser, options.Properties);
1079 
1080   if (parser[NKey::kNtSecurity].ThereIs) options.NtSecurity.SetTrueTrue();
1081 
1082   SetBoolPair(parser, NKey::kAltStreams, options.AltStreams);
1083   SetBoolPair(parser, NKey::kHardLinks, options.HardLinks);
1084   SetBoolPair(parser, NKey::kSymLinks, options.SymLinks);
1085 
1086   if (isExtractOrList)
1087   {
1088     CExtractOptionsBase &eo = options.ExtractOptions;
1089 
1090     {
1091       CExtractNtOptions &nt = eo.NtOptions;
1092       nt.NtSecurity = options.NtSecurity;
1093 
1094       nt.AltStreams = options.AltStreams;
1095       if (!options.AltStreams.Def)
1096         nt.AltStreams.Val = true;
1097 
1098       nt.HardLinks = options.HardLinks;
1099       if (!options.HardLinks.Def)
1100         nt.HardLinks.Val = true;
1101 
1102       nt.SymLinks = options.SymLinks;
1103       if (!options.SymLinks.Def)
1104         nt.SymLinks.Val = true;
1105 
1106       nt.ReplaceColonForAltStream = parser[NKey::kReplaceColonForAltStream].ThereIs;
1107       nt.WriteToAltStreamIfColon = parser[NKey::kWriteToAltStreamIfColon].ThereIs;
1108     }
1109 
1110     options.Censor.AddPathsToCensor(NWildcard::k_AbsPath);
1111     options.Censor.ExtendExclude();
1112 
1113     // are there paths that look as non-relative (!Prefix.IsEmpty())
1114     if (!options.Censor.AllAreRelative())
1115       throw CArcCmdLineException("Cannot use absolute pathnames for this command");
1116 
1117     NWildcard::CCensor &arcCensor = options.arcCensor;
1118 
1119     if (parser[NKey::kArInclude].ThereIs)
1120       AddSwitchWildcardsToCensor(arcCensor, parser[NKey::kArInclude].PostStrings, true, NRecursedType::kNonRecursed, wildcardMatching, codePage);
1121     if (parser[NKey::kArExclude].ThereIs)
1122       AddSwitchWildcardsToCensor(arcCensor, parser[NKey::kArExclude].PostStrings, false, NRecursedType::kNonRecursed, wildcardMatching, codePage);
1123 
1124     if (thereIsArchiveName)
1125       AddNameToCensor(arcCensor, options.ArchiveName, true, NRecursedType::kNonRecursed, wildcardMatching);
1126 
1127     arcCensor.AddPathsToCensor(NWildcard::k_RelatPath);
1128 
1129     #ifdef _WIN32
1130     ConvertToLongNames(arcCensor);
1131     #endif
1132 
1133     arcCensor.ExtendExclude();
1134 
1135     if (options.StdInMode)
1136       options.ArcName_for_StdInMode = parser[NKey::kStdIn].PostStrings.Front();
1137 
1138     if (isExtractGroupCommand)
1139     {
1140       if (options.StdOutMode)
1141       {
1142         if (
1143                   options.Number_for_Percents == k_OutStream_stdout
1144             // || options.Number_for_Out      == k_OutStream_stdout
1145             // || options.Number_for_Errors   == k_OutStream_stdout
1146             ||
1147             (
1148               (options.IsStdOutTerminal && options.IsStdErrTerminal)
1149               &&
1150               (
1151                       options.Number_for_Percents != k_OutStream_disabled
1152                 // || options.Number_for_Out      != k_OutStream_disabled
1153                 // || options.Number_for_Errors   != k_OutStream_disabled
1154               )
1155             )
1156            )
1157           throw CArcCmdLineException(kSameTerminalError);
1158       }
1159 
1160       if (parser[NKey::kOutputDir].ThereIs)
1161       {
1162         eo.OutputDir = us2fs(parser[NKey::kOutputDir].PostStrings[0]);
1163         NFile::NName::NormalizeDirPathPrefix(eo.OutputDir);
1164       }
1165 
1166       eo.OverwriteMode = NExtract::NOverwriteMode::kAsk;
1167       if (parser[NKey::kOverwrite].ThereIs)
1168       {
1169         eo.OverwriteMode = k_OverwriteModes[(unsigned)parser[NKey::kOverwrite].PostCharIndex];
1170         eo.OverwriteMode_Force = true;
1171       }
1172       else if (options.YesToAll)
1173       {
1174         eo.OverwriteMode = NExtract::NOverwriteMode::kOverwrite;
1175         eo.OverwriteMode_Force = true;
1176       }
1177     }
1178 
1179     eo.PathMode = options.Command.GetPathMode();
1180     if (censorPathMode == NWildcard::k_AbsPath)
1181     {
1182       eo.PathMode = NExtract::NPathMode::kAbsPaths;
1183       eo.PathMode_Force = true;
1184     }
1185     else if (censorPathMode == NWildcard::k_FullPath)
1186     {
1187       eo.PathMode = NExtract::NPathMode::kFullPaths;
1188       eo.PathMode_Force = true;
1189     }
1190   }
1191   else if (options.Command.IsFromUpdateGroup())
1192   {
1193     if (parser[NKey::kArInclude].ThereIs)
1194       throw CArcCmdLineException("-ai switch is not supported for this command");
1195 
1196     CUpdateOptions &updateOptions = options.UpdateOptions;
1197 
1198     SetAddCommandOptions(options.Command.CommandType, parser, updateOptions);
1199 
1200     updateOptions.MethodMode.Properties = options.Properties;
1201 
1202     if (parser[NKey::kShareForWrite].ThereIs)
1203       updateOptions.OpenShareForWrite = true;
1204     if (parser[NKey::kStopAfterOpenError].ThereIs)
1205       updateOptions.StopAfterOpenError = true;
1206 
1207     updateOptions.PathMode = censorPathMode;
1208 
1209     updateOptions.AltStreams = options.AltStreams;
1210     updateOptions.NtSecurity = options.NtSecurity;
1211     updateOptions.HardLinks = options.HardLinks;
1212     updateOptions.SymLinks = options.SymLinks;
1213 
1214     updateOptions.EMailMode = parser[NKey::kEmail].ThereIs;
1215     if (updateOptions.EMailMode)
1216     {
1217       updateOptions.EMailAddress = parser[NKey::kEmail].PostStrings.Front();
1218       if (updateOptions.EMailAddress.Len() > 0)
1219         if (updateOptions.EMailAddress[0] == L'.')
1220         {
1221           updateOptions.EMailRemoveAfter = true;
1222           updateOptions.EMailAddress.Delete(0);
1223         }
1224     }
1225 
1226     updateOptions.StdOutMode = options.StdOutMode;
1227     updateOptions.StdInMode = options.StdInMode;
1228 
1229     updateOptions.DeleteAfterCompressing = parser[NKey::kDeleteAfterCompressing].ThereIs;
1230     updateOptions.SetArcMTime = parser[NKey::kSetArcMTime].ThereIs;
1231 
1232     if (updateOptions.StdOutMode && updateOptions.EMailMode)
1233       throw CArcCmdLineException("stdout mode and email mode cannot be combined");
1234 
1235     if (updateOptions.StdOutMode)
1236     {
1237       if (options.IsStdOutTerminal)
1238         throw CArcCmdLineException(kTerminalOutError);
1239 
1240       if (options.Number_for_Percents == k_OutStream_stdout
1241           || options.Number_for_Out == k_OutStream_stdout
1242           || options.Number_for_Errors == k_OutStream_stdout)
1243         throw CArcCmdLineException(kSameTerminalError);
1244     }
1245 
1246     if (updateOptions.StdInMode)
1247       updateOptions.StdInFileName = parser[NKey::kStdIn].PostStrings.Front();
1248 
1249     if (options.Command.CommandType == NCommandType::kRename)
1250       if (updateOptions.Commands.Size() != 1)
1251         throw CArcCmdLineException("Only one archive can be created with rename command");
1252   }
1253   else if (options.Command.CommandType == NCommandType::kBenchmark)
1254   {
1255     options.NumIterations = 1;
1256     if (curCommandIndex < numNonSwitchStrings)
1257     {
1258       if (!StringToUInt32(nonSwitchStrings[curCommandIndex], options.NumIterations))
1259         throw CArcCmdLineException("Incorrect Number of benmchmark iterations", nonSwitchStrings[curCommandIndex]);
1260       curCommandIndex++;
1261     }
1262   }
1263   else if (options.Command.CommandType == NCommandType::kHash)
1264   {
1265     options.Censor.AddPathsToCensor(censorPathMode);
1266     options.Censor.ExtendExclude();
1267 
1268     CHashOptions &hashOptions = options.HashOptions;
1269     hashOptions.PathMode = censorPathMode;
1270     hashOptions.Methods = options.HashMethods;
1271     if (parser[NKey::kShareForWrite].ThereIs)
1272       hashOptions.OpenShareForWrite = true;
1273     hashOptions.StdInMode = options.StdInMode;
1274     hashOptions.AltStreamsMode = options.AltStreams.Val;
1275   }
1276   else if (options.Command.CommandType == NCommandType::kInfo)
1277   {
1278   }
1279   else
1280     throw 20150919;
1281 }
1282