ArchiveCommandLine.cpp (37286B)
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 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 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 287 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 299 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 310 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 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 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 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 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 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 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 485 CEventSetEnd(const wchar_t *name): Name(name) {} 486 ~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 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 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 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 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 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 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 789 static inline void SetStreamMode(const CSwitchResult &sw, unsigned &res) 790 { 791 if (sw.ThereIs) 792 res = sw.PostCharIndex; 793 } 794 795 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 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 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 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 }