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