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