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