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