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