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 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 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 290 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 302 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 313 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 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 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 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 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 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 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 499 CEventSetEnd(const wchar_t *name): Name(name) {} 500 ~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 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 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 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 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 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 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 803 static inline void SetStreamMode(const CSwitchResult &sw, unsigned &res) 804 { 805 if (sw.ThereIs) 806 res = sw.PostCharIndex; 807 } 808 809 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 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 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 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