1 // ArchiveCommandLine.cpp 2 3 #include "StdAfx.h" 4 5 #ifdef _WIN32 6 #ifndef UNDER_CE 7 #include <io.h> 8 #endif 9 #endif 10 #include <stdio.h> 11 12 #include "Common/ListFileUtils.h" 13 #include "Common/StringConvert.h" 14 #include "Common/StringToInt.h" 15 16 #include "Windows/FileDir.h" 17 #include "Windows/FileName.h" 18 #ifdef _WIN32 19 #include "Windows/FileMapping.h" 20 #include "Windows/Synchronization.h" 21 #endif 22 23 #include "ArchiveCommandLine.h" 24 #include "EnumDirItems.h" 25 #include "SortUtils.h" 26 #include "Update.h" 27 #include "UpdateAction.h" 28 29 extern bool g_CaseSensitive; 30 31 #ifdef UNDER_CE 32 33 #define MY_IS_TERMINAL(x) false; 34 35 #else 36 37 #if _MSC_VER >= 1400 38 #define MY_isatty_fileno(x) _isatty(_fileno(x)) 39 #else 40 #define MY_isatty_fileno(x) isatty(fileno(x)) 41 #endif 42 43 #define MY_IS_TERMINAL(x) (MY_isatty_fileno(x) != 0); 44 45 #endif 46 47 using namespace NCommandLineParser; 48 using namespace NWindows; 49 using namespace NFile; 50 51 int g_CodePage = -1; 52 53 namespace NKey { 54 enum Enum 55 { 56 kHelp1 = 0, 57 kHelp2, 58 kHelp3, 59 kDisableHeaders, 60 kDisablePercents, 61 kArchiveType, 62 kYes, 63 #ifndef _NO_CRYPTO 64 kPassword, 65 #endif 66 kProperty, 67 kOutputDir, 68 kWorkingDir, 69 kInclude, 70 kExclude, 71 kArInclude, 72 kArExclude, 73 kNoArName, 74 kUpdate, 75 kVolume, 76 kRecursed, 77 kSfx, 78 kStdIn, 79 kStdOut, 80 kOverwrite, 81 kEmail, 82 kShowDialog, 83 kLargePages, 84 kListfileCharSet, 85 kConsoleCharSet, 86 kTechMode, 87 kShareForWrite, 88 kCaseSensitive, 89 kCalcCrc 90 }; 91 92 } 93 94 95 static const wchar_t kRecursedIDChar = 'R'; 96 static const wchar_t *kRecursedPostCharSet = L"0-"; 97 98 namespace NRecursedPostCharIndex { 99 enum EEnum 100 { 101 kWildCardRecursionOnly = 0, 102 kNoRecursion = 1 103 }; 104 } 105 106 static const char kImmediateNameID = '!'; 107 static const char kMapNameID = '#'; 108 static const char kFileListID = '@'; 109 110 static const char kSomeCludePostStringMinSize = 2; // at least <@|!><N>ame must be 111 static const char kSomeCludeAfterRecursedPostStringMinSize = 2; // at least <@|!><N>ame must be 112 113 static const wchar_t *kOverwritePostCharSet = L"asut"; 114 115 NExtract::NOverwriteMode::EEnum k_OverwriteModes[] = 116 { 117 NExtract::NOverwriteMode::kWithoutPrompt, 118 NExtract::NOverwriteMode::kSkipExisting, 119 NExtract::NOverwriteMode::kAutoRename, 120 NExtract::NOverwriteMode::kAutoRenameExisting 121 }; 122 123 static const CSwitchForm kSwitchForms[] = 124 { 125 { L"?", NSwitchType::kSimple, false }, 126 { L"H", NSwitchType::kSimple, false }, 127 { L"-HELP", NSwitchType::kSimple, false }, 128 { L"BA", NSwitchType::kSimple, false }, 129 { L"BD", NSwitchType::kSimple, false }, 130 { L"T", NSwitchType::kUnLimitedPostString, false, 1 }, 131 { L"Y", NSwitchType::kSimple, false }, 132 #ifndef _NO_CRYPTO 133 { L"P", NSwitchType::kUnLimitedPostString, false, 0 }, 134 #endif 135 { L"M", NSwitchType::kUnLimitedPostString, true, 1 }, 136 { L"O", NSwitchType::kUnLimitedPostString, false, 1 }, 137 { L"W", NSwitchType::kUnLimitedPostString, false, 0 }, 138 { L"I", NSwitchType::kUnLimitedPostString, true, kSomeCludePostStringMinSize}, 139 { L"X", NSwitchType::kUnLimitedPostString, true, kSomeCludePostStringMinSize}, 140 { L"AI", NSwitchType::kUnLimitedPostString, true, kSomeCludePostStringMinSize}, 141 { L"AX", NSwitchType::kUnLimitedPostString, true, kSomeCludePostStringMinSize}, 142 { L"AN", NSwitchType::kSimple, false }, 143 { L"U", NSwitchType::kUnLimitedPostString, true, 1}, 144 { L"V", NSwitchType::kUnLimitedPostString, true, 1}, 145 { L"R", NSwitchType::kPostChar, false, 0, 0, kRecursedPostCharSet }, 146 { L"SFX", NSwitchType::kUnLimitedPostString, false, 0 }, 147 { L"SI", NSwitchType::kUnLimitedPostString, false, 0 }, 148 { L"SO", NSwitchType::kSimple, false, 0 }, 149 { L"AO", NSwitchType::kPostChar, false, 1, 1, kOverwritePostCharSet}, 150 { L"SEML", NSwitchType::kUnLimitedPostString, false, 0}, 151 { L"AD", NSwitchType::kSimple, false }, 152 { L"SLP", NSwitchType::kUnLimitedPostString, false, 0}, 153 { L"SCS", NSwitchType::kUnLimitedPostString, false, 0}, 154 { L"SCC", NSwitchType::kUnLimitedPostString, false, 0}, 155 { L"SLT", NSwitchType::kSimple, false }, 156 { L"SSW", NSwitchType::kSimple, false }, 157 { L"SSC", NSwitchType::kPostChar, false, 0, 0, L"-" }, 158 { L"SCRC", NSwitchType::kSimple, false } 159 }; 160 161 static const CCommandForm g_CommandForms[] = 162 { 163 { L"A", false }, 164 { L"U", false }, 165 { L"D", false }, 166 { L"T", false }, 167 { L"E", false }, 168 { L"X", false }, 169 { L"L", false }, 170 { L"B", false }, 171 { L"I", false } 172 }; 173 174 static const int kNumCommandForms = sizeof(g_CommandForms) / sizeof(g_CommandForms[0]); 175 176 static const wchar_t *kUniversalWildcard = L"*"; 177 static const int kMinNonSwitchWords = 1; 178 static const int kCommandIndex = 0; 179 180 // --------------------------- 181 // exception messages 182 183 static const char *kUserErrorMessage = "Incorrect command line"; 184 static const char *kCannotFindListFile = "Cannot find listfile"; 185 static const char *kIncorrectListFile = "Incorrect item in listfile.\nCheck charset encoding and -scs switch."; 186 static const char *kIncorrectWildCardInListFile = "Incorrect wildcard in listfile"; 187 static const char *kIncorrectWildCardInCommandLine = "Incorrect wildcard in command line"; 188 static const char *kTerminalOutError = "I won't write compressed data to a terminal"; 189 static const char *kSameTerminalError = "I won't write data and program's messages to same terminal"; 190 static const char *kEmptyFilePath = "Empty file path"; 191 192 static void ThrowException(const char *errorMessage) 193 { 194 throw CArchiveCommandLineException(errorMessage); 195 } 196 197 static void ThrowUserErrorException() 198 { 199 ThrowException(kUserErrorMessage); 200 } 201 202 // --------------------------- 203 204 bool CArchiveCommand::IsFromExtractGroup() const 205 { 206 switch(CommandType) 207 { 208 case NCommandType::kTest: 209 case NCommandType::kExtract: 210 case NCommandType::kFullExtract: 211 return true; 212 default: 213 return false; 214 } 215 } 216 217 NExtract::NPathMode::EEnum CArchiveCommand::GetPathMode() const 218 { 219 switch(CommandType) 220 { 221 case NCommandType::kTest: 222 case NCommandType::kFullExtract: 223 return NExtract::NPathMode::kFullPathnames; 224 default: 225 return NExtract::NPathMode::kNoPathnames; 226 } 227 } 228 229 bool CArchiveCommand::IsFromUpdateGroup() const 230 { 231 return (CommandType == NCommandType::kAdd || 232 CommandType == NCommandType::kUpdate || 233 CommandType == NCommandType::kDelete); 234 } 235 236 static NRecursedType::EEnum GetRecursedTypeFromIndex(int index) 237 { 238 switch (index) 239 { 240 case NRecursedPostCharIndex::kWildCardRecursionOnly: 241 return NRecursedType::kWildCardOnlyRecursed; 242 case NRecursedPostCharIndex::kNoRecursion: 243 return NRecursedType::kNonRecursed; 244 default: 245 return NRecursedType::kRecursed; 246 } 247 } 248 249 static bool ParseArchiveCommand(const UString &commandString, CArchiveCommand &command) 250 { 251 UString commandStringUpper = commandString; 252 commandStringUpper.MakeUpper(); 253 UString postString; 254 int commandIndex = ParseCommand(kNumCommandForms, g_CommandForms, commandStringUpper, 255 postString) ; 256 if (commandIndex < 0) 257 return false; 258 command.CommandType = (NCommandType::EEnum)commandIndex; 259 return true; 260 } 261 262 // ------------------------------------------------------------------ 263 // filenames functions 264 265 static void AddNameToCensor(NWildcard::CCensor &wildcardCensor, 266 const UString &name, bool include, NRecursedType::EEnum type) 267 { 268 bool recursed = false; 269 270 switch (type) 271 { 272 case NRecursedType::kWildCardOnlyRecursed: 273 recursed = DoesNameContainWildCard(name); 274 break; 275 case NRecursedType::kRecursed: 276 recursed = true; 277 break; 278 } 279 wildcardCensor.AddItem(include, name, recursed); 280 } 281 282 static void AddToCensorFromListFile(NWildcard::CCensor &wildcardCensor, 283 LPCWSTR fileName, bool include, NRecursedType::EEnum type, UINT codePage) 284 { 285 UStringVector names; 286 if (!NFind::DoesFileExist(fileName)) 287 throw kCannotFindListFile; 288 if (!ReadNamesFromListFile(fileName, names, codePage)) 289 throw kIncorrectListFile; 290 for (int i = 0; i < names.Size(); i++) 291 AddNameToCensor(wildcardCensor, names[i], include, type); 292 } 293 294 static void AddToCensorFromNonSwitchesStrings( 295 int startIndex, 296 NWildcard::CCensor &wildcardCensor, 297 const UStringVector &nonSwitchStrings, NRecursedType::EEnum type, 298 bool thereAreSwitchIncludes, UINT codePage) 299 { 300 if (nonSwitchStrings.Size() == startIndex && (!thereAreSwitchIncludes)) 301 AddNameToCensor(wildcardCensor, kUniversalWildcard, true, type); 302 for (int i = startIndex; i < nonSwitchStrings.Size(); i++) 303 { 304 const UString &s = nonSwitchStrings[i]; 305 if (s.IsEmpty()) 306 throw kEmptyFilePath; 307 if (s[0] == kFileListID) 308 AddToCensorFromListFile(wildcardCensor, s.Mid(1), true, type, codePage); 309 else 310 AddNameToCensor(wildcardCensor, s, true, type); 311 } 312 } 313 314 #ifdef _WIN32 315 static void ParseMapWithPaths(NWildcard::CCensor &wildcardCensor, 316 const UString &switchParam, bool include, 317 NRecursedType::EEnum commonRecursedType) 318 { 319 int splitPos = switchParam.Find(L':'); 320 if (splitPos < 0) 321 ThrowUserErrorException(); 322 UString mappingName = switchParam.Left(splitPos); 323 324 UString switchParam2 = switchParam.Mid(splitPos + 1); 325 splitPos = switchParam2.Find(L':'); 326 if (splitPos < 0) 327 ThrowUserErrorException(); 328 329 UString mappingSize = switchParam2.Left(splitPos); 330 UString eventName = switchParam2.Mid(splitPos + 1); 331 332 UInt64 dataSize64 = ConvertStringToUInt64(mappingSize, NULL); 333 UInt32 dataSize = (UInt32)dataSize64; 334 { 335 CFileMapping fileMapping; 336 if (fileMapping.Open(FILE_MAP_READ, GetSystemString(mappingName)) != 0) 337 ThrowException("Can not open mapping"); 338 LPVOID data = fileMapping.Map(FILE_MAP_READ, 0, dataSize); 339 if (data == NULL) 340 ThrowException("MapViewOfFile error"); 341 try 342 { 343 const wchar_t *curData = (const wchar_t *)data; 344 if (*curData != 0) 345 ThrowException("Incorrect mapping data"); 346 UInt32 numChars = dataSize / sizeof(wchar_t); 347 UString name; 348 for (UInt32 i = 1; i < numChars; i++) 349 { 350 wchar_t c = curData[i]; 351 if (c == L'\0') 352 { 353 AddNameToCensor(wildcardCensor, name, include, commonRecursedType); 354 name.Empty(); 355 } 356 else 357 name += c; 358 } 359 if (!name.IsEmpty()) 360 ThrowException("data error"); 361 } 362 catch(...) 363 { 364 UnmapViewOfFile(data); 365 throw; 366 } 367 UnmapViewOfFile(data); 368 } 369 370 { 371 NSynchronization::CManualResetEvent event; 372 if (event.Open(EVENT_MODIFY_STATE, false, GetSystemString(eventName)) == S_OK) 373 event.Set(); 374 } 375 } 376 #endif 377 378 static void AddSwitchWildCardsToCensor(NWildcard::CCensor &wildcardCensor, 379 const UStringVector &strings, bool include, 380 NRecursedType::EEnum commonRecursedType, UINT codePage) 381 { 382 for (int i = 0; i < strings.Size(); i++) 383 { 384 const UString &name = strings[i]; 385 NRecursedType::EEnum recursedType; 386 int pos = 0; 387 if (name.Length() < kSomeCludePostStringMinSize) 388 ThrowUserErrorException(); 389 if (::MyCharUpper(name[pos]) == kRecursedIDChar) 390 { 391 pos++; 392 int index = UString(kRecursedPostCharSet).Find(name[pos]); 393 recursedType = GetRecursedTypeFromIndex(index); 394 if (index >= 0) 395 pos++; 396 } 397 else 398 recursedType = commonRecursedType; 399 if (name.Length() < pos + kSomeCludeAfterRecursedPostStringMinSize) 400 ThrowUserErrorException(); 401 UString tail = name.Mid(pos + 1); 402 if (name[pos] == kImmediateNameID) 403 AddNameToCensor(wildcardCensor, tail, include, recursedType); 404 else if (name[pos] == kFileListID) 405 AddToCensorFromListFile(wildcardCensor, tail, include, recursedType, codePage); 406 #ifdef _WIN32 407 else if (name[pos] == kMapNameID) 408 ParseMapWithPaths(wildcardCensor, tail, include, recursedType); 409 #endif 410 else 411 ThrowUserErrorException(); 412 } 413 } 414 415 #ifdef _WIN32 416 417 // This code converts all short file names to long file names. 418 419 static void ConvertToLongName(const UString &prefix, UString &name) 420 { 421 if (name.IsEmpty() || DoesNameContainWildCard(name)) 422 return; 423 NFind::CFileInfoW fi; 424 if (fi.Find(prefix + name)) 425 name = fi.Name; 426 } 427 428 static void ConvertToLongNames(const UString &prefix, CObjectVector<NWildcard::CItem> &items) 429 { 430 for (int i = 0; i < items.Size(); i++) 431 { 432 NWildcard::CItem &item = items[i]; 433 if (item.Recursive || item.PathParts.Size() != 1) 434 continue; 435 ConvertToLongName(prefix, item.PathParts.Front()); 436 } 437 } 438 439 static void ConvertToLongNames(const UString &prefix, NWildcard::CCensorNode &node) 440 { 441 ConvertToLongNames(prefix, node.IncludeItems); 442 ConvertToLongNames(prefix, node.ExcludeItems); 443 int i; 444 for (i = 0; i < node.SubNodes.Size(); i++) 445 ConvertToLongName(prefix, node.SubNodes[i].Name); 446 // mix folders with same name 447 for (i = 0; i < node.SubNodes.Size(); i++) 448 { 449 NWildcard::CCensorNode &nextNode1 = node.SubNodes[i]; 450 for (int j = i + 1; j < node.SubNodes.Size();) 451 { 452 const NWildcard::CCensorNode &nextNode2 = node.SubNodes[j]; 453 if (nextNode1.Name.CompareNoCase(nextNode2.Name) == 0) 454 { 455 nextNode1.IncludeItems += nextNode2.IncludeItems; 456 nextNode1.ExcludeItems += nextNode2.ExcludeItems; 457 node.SubNodes.Delete(j); 458 } 459 else 460 j++; 461 } 462 } 463 for (i = 0; i < node.SubNodes.Size(); i++) 464 { 465 NWildcard::CCensorNode &nextNode = node.SubNodes[i]; 466 ConvertToLongNames(prefix + nextNode.Name + wchar_t(NFile::NName::kDirDelimiter), nextNode); 467 } 468 } 469 470 static void ConvertToLongNames(NWildcard::CCensor &censor) 471 { 472 for (int i = 0; i < censor.Pairs.Size(); i++) 473 { 474 NWildcard::CPair &pair = censor.Pairs[i]; 475 ConvertToLongNames(pair.Prefix, pair.Head); 476 } 477 } 478 479 #endif 480 481 static NUpdateArchive::NPairAction::EEnum GetUpdatePairActionType(int i) 482 { 483 switch(i) 484 { 485 case NUpdateArchive::NPairAction::kIgnore: return NUpdateArchive::NPairAction::kIgnore; 486 case NUpdateArchive::NPairAction::kCopy: return NUpdateArchive::NPairAction::kCopy; 487 case NUpdateArchive::NPairAction::kCompress: return NUpdateArchive::NPairAction::kCompress; 488 case NUpdateArchive::NPairAction::kCompressAsAnti: return NUpdateArchive::NPairAction::kCompressAsAnti; 489 } 490 throw 98111603; 491 } 492 493 const UString kUpdatePairStateIDSet = L"PQRXYZW"; 494 const int kUpdatePairStateNotSupportedActions[] = {2, 2, 1, -1, -1, -1, -1}; 495 496 const UString kUpdatePairActionIDSet = L"0123"; //Ignore, Copy, Compress, Create Anti 497 498 const wchar_t *kUpdateIgnoreItselfPostStringID = L"-"; 499 const wchar_t kUpdateNewArchivePostCharID = '!'; 500 501 502 static bool ParseUpdateCommandString2(const UString &command, 503 NUpdateArchive::CActionSet &actionSet, UString &postString) 504 { 505 for (int i = 0; i < command.Length();) 506 { 507 wchar_t c = MyCharUpper(command[i]); 508 int statePos = kUpdatePairStateIDSet.Find(c); 509 if (statePos < 0) 510 { 511 postString = command.Mid(i); 512 return true; 513 } 514 i++; 515 if (i >= command.Length()) 516 return false; 517 int actionPos = kUpdatePairActionIDSet.Find(::MyCharUpper(command[i])); 518 if (actionPos < 0) 519 return false; 520 actionSet.StateActions[statePos] = GetUpdatePairActionType(actionPos); 521 if (kUpdatePairStateNotSupportedActions[statePos] == actionPos) 522 return false; 523 i++; 524 } 525 postString.Empty(); 526 return true; 527 } 528 529 static void ParseUpdateCommandString(CUpdateOptions &options, 530 const UStringVector &updatePostStrings, 531 const NUpdateArchive::CActionSet &defaultActionSet) 532 { 533 for (int i = 0; i < updatePostStrings.Size(); i++) 534 { 535 const UString &updateString = updatePostStrings[i]; 536 if (updateString.CompareNoCase(kUpdateIgnoreItselfPostStringID) == 0) 537 { 538 if (options.UpdateArchiveItself) 539 { 540 options.UpdateArchiveItself = false; 541 options.Commands.Delete(0); 542 } 543 } 544 else 545 { 546 NUpdateArchive::CActionSet actionSet = defaultActionSet; 547 548 UString postString; 549 if (!ParseUpdateCommandString2(updateString, actionSet, postString)) 550 ThrowUserErrorException(); 551 if (postString.IsEmpty()) 552 { 553 if (options.UpdateArchiveItself) 554 options.Commands[0].ActionSet = actionSet; 555 } 556 else 557 { 558 if (MyCharUpper(postString[0]) != kUpdateNewArchivePostCharID) 559 ThrowUserErrorException(); 560 CUpdateArchiveCommand uc; 561 UString archivePath = postString.Mid(1); 562 if (archivePath.IsEmpty()) 563 ThrowUserErrorException(); 564 uc.UserArchivePath = archivePath; 565 uc.ActionSet = actionSet; 566 options.Commands.Add(uc); 567 } 568 } 569 } 570 } 571 572 static const char kByteSymbol = 'B'; 573 static const char kKiloSymbol = 'K'; 574 static const char kMegaSymbol = 'M'; 575 static const char kGigaSymbol = 'G'; 576 577 static bool ParseComplexSize(const UString &src, UInt64 &result) 578 { 579 UString s = src; 580 s.MakeUpper(); 581 582 const wchar_t *start = s; 583 const wchar_t *end; 584 UInt64 number = ConvertStringToUInt64(start, &end); 585 int numDigits = (int)(end - start); 586 if (numDigits == 0 || s.Length() > numDigits + 1) 587 return false; 588 if (s.Length() == numDigits) 589 { 590 result = number; 591 return true; 592 } 593 int numBits; 594 switch (s[numDigits]) 595 { 596 case kByteSymbol: 597 result = number; 598 return true; 599 case kKiloSymbol: 600 numBits = 10; 601 break; 602 case kMegaSymbol: 603 numBits = 20; 604 break; 605 case kGigaSymbol: 606 numBits = 30; 607 break; 608 default: 609 return false; 610 } 611 if (number >= ((UInt64)1 << (64 - numBits))) 612 return false; 613 result = number << numBits; 614 return true; 615 } 616 617 static void SetAddCommandOptions( 618 NCommandType::EEnum commandType, 619 const CParser &parser, 620 CUpdateOptions &options) 621 { 622 NUpdateArchive::CActionSet defaultActionSet; 623 switch(commandType) 624 { 625 case NCommandType::kAdd: 626 defaultActionSet = NUpdateArchive::kAddActionSet; 627 break; 628 case NCommandType::kDelete: 629 defaultActionSet = NUpdateArchive::kDeleteActionSet; 630 break; 631 default: 632 defaultActionSet = NUpdateArchive::kUpdateActionSet; 633 } 634 635 options.UpdateArchiveItself = true; 636 637 options.Commands.Clear(); 638 CUpdateArchiveCommand updateMainCommand; 639 updateMainCommand.ActionSet = defaultActionSet; 640 options.Commands.Add(updateMainCommand); 641 if (parser[NKey::kUpdate].ThereIs) 642 ParseUpdateCommandString(options, parser[NKey::kUpdate].PostStrings, 643 defaultActionSet); 644 if (parser[NKey::kWorkingDir].ThereIs) 645 { 646 const UString &postString = parser[NKey::kWorkingDir].PostStrings[0]; 647 if (postString.IsEmpty()) 648 NDirectory::MyGetTempPath(options.WorkingDir); 649 else 650 options.WorkingDir = postString; 651 } 652 options.SfxMode = parser[NKey::kSfx].ThereIs; 653 if (options.SfxMode) 654 options.SfxModule = parser[NKey::kSfx].PostStrings[0]; 655 656 if (parser[NKey::kVolume].ThereIs) 657 { 658 const UStringVector &sv = parser[NKey::kVolume].PostStrings; 659 for (int i = 0; i < sv.Size(); i++) 660 { 661 UInt64 size; 662 if (!ParseComplexSize(sv[i], size)) 663 ThrowException("Incorrect volume size"); 664 options.VolumesSizes.Add(size); 665 } 666 } 667 } 668 669 static void SetMethodOptions(const CParser &parser, CObjectVector<CProperty> &properties) 670 { 671 if (parser[NKey::kProperty].ThereIs) 672 { 673 // options.MethodMode.Properties.Clear(); 674 for (int i = 0; i < parser[NKey::kProperty].PostStrings.Size(); i++) 675 { 676 CProperty property; 677 const UString &postString = parser[NKey::kProperty].PostStrings[i]; 678 int index = postString.Find(L'='); 679 if (index < 0) 680 property.Name = postString; 681 else 682 { 683 property.Name = postString.Left(index); 684 property.Value = postString.Mid(index + 1); 685 } 686 properties.Add(property); 687 } 688 } 689 } 690 691 CArchiveCommandLineParser::CArchiveCommandLineParser(): 692 parser(sizeof(kSwitchForms) / sizeof(kSwitchForms[0])) {} 693 694 void CArchiveCommandLineParser::Parse1(const UStringVector &commandStrings, 695 CArchiveCommandLineOptions &options) 696 { 697 try 698 { 699 parser.ParseStrings(kSwitchForms, commandStrings); 700 } 701 catch(...) 702 { 703 ThrowUserErrorException(); 704 } 705 706 options.IsInTerminal = MY_IS_TERMINAL(stdin); 707 options.IsStdOutTerminal = MY_IS_TERMINAL(stdout); 708 options.IsStdErrTerminal = MY_IS_TERMINAL(stderr); 709 options.StdInMode = parser[NKey::kStdIn].ThereIs; 710 options.StdOutMode = parser[NKey::kStdOut].ThereIs; 711 options.EnableHeaders = !parser[NKey::kDisableHeaders].ThereIs; 712 options.HelpMode = parser[NKey::kHelp1].ThereIs || parser[NKey::kHelp2].ThereIs || parser[NKey::kHelp3].ThereIs; 713 714 #ifdef _WIN32 715 options.LargePages = false; 716 if (parser[NKey::kLargePages].ThereIs) 717 { 718 const UString &postString = parser[NKey::kLargePages].PostStrings.Front(); 719 if (postString.IsEmpty()) 720 options.LargePages = true; 721 } 722 #endif 723 } 724 725 struct CCodePagePair 726 { 727 const wchar_t *Name; 728 UINT CodePage; 729 }; 730 731 static CCodePagePair g_CodePagePairs[] = 732 { 733 { L"UTF-8", CP_UTF8 }, 734 { L"WIN", CP_ACP }, 735 { L"DOS", CP_OEMCP } 736 }; 737 738 static int FindCharset(const NCommandLineParser::CParser &parser, int keyIndex, int defaultVal) 739 { 740 if (!parser[keyIndex].ThereIs) 741 return defaultVal; 742 743 UString name = parser[keyIndex].PostStrings.Back(); 744 name.MakeUpper(); 745 int i; 746 for (i = 0; i < sizeof(g_CodePagePairs) / sizeof(g_CodePagePairs[0]); i++) 747 { 748 const CCodePagePair &pair = g_CodePagePairs[i]; 749 if (name.Compare(pair.Name) == 0) 750 return pair.CodePage; 751 } 752 if (i == sizeof(g_CodePagePairs) / sizeof(g_CodePagePairs[0])) 753 ThrowUserErrorException(); 754 return -1; 755 } 756 757 static bool ConvertStringToUInt32(const wchar_t *s, UInt32 &v) 758 { 759 const wchar_t *end; 760 UInt64 number = ConvertStringToUInt64(s, &end); 761 if (*end != 0) 762 return false; 763 if (number > (UInt32)0xFFFFFFFF) 764 return false; 765 v = (UInt32)number; 766 return true; 767 } 768 769 void EnumerateDirItemsAndSort(NWildcard::CCensor &wildcardCensor, 770 UStringVector &sortedPaths, 771 UStringVector &sortedFullPaths) 772 { 773 UStringVector paths; 774 { 775 CDirItems dirItems; 776 { 777 UStringVector errorPaths; 778 CRecordVector<DWORD> errorCodes; 779 HRESULT res = EnumerateItems(wildcardCensor, dirItems, NULL, errorPaths, errorCodes); 780 if (res != S_OK || errorPaths.Size() > 0) 781 throw "cannot find archive"; 782 } 783 for (int i = 0; i < dirItems.Items.Size(); i++) 784 { 785 const CDirItem &dirItem = dirItems.Items[i]; 786 if (!dirItem.IsDir()) 787 paths.Add(dirItems.GetPhyPath(i)); 788 } 789 } 790 791 if (paths.Size() == 0) 792 throw "there is no such archive"; 793 794 UStringVector fullPaths; 795 796 int i; 797 for (i = 0; i < paths.Size(); i++) 798 { 799 UString fullPath; 800 NFile::NDirectory::MyGetFullPathName(paths[i], fullPath); 801 fullPaths.Add(fullPath); 802 } 803 CIntVector indices; 804 SortFileNames(fullPaths, indices); 805 sortedPaths.Reserve(indices.Size()); 806 sortedFullPaths.Reserve(indices.Size()); 807 for (i = 0; i < indices.Size(); i++) 808 { 809 int index = indices[i]; 810 sortedPaths.Add(paths[index]); 811 sortedFullPaths.Add(fullPaths[index]); 812 } 813 } 814 815 void CArchiveCommandLineParser::Parse2(CArchiveCommandLineOptions &options) 816 { 817 const UStringVector &nonSwitchStrings = parser.NonSwitchStrings; 818 int numNonSwitchStrings = nonSwitchStrings.Size(); 819 if (numNonSwitchStrings < kMinNonSwitchWords) 820 ThrowUserErrorException(); 821 822 if (!ParseArchiveCommand(nonSwitchStrings[kCommandIndex], options.Command)) 823 ThrowUserErrorException(); 824 825 options.TechMode = parser[NKey::kTechMode].ThereIs; 826 options.CalcCrc = parser[NKey::kCalcCrc].ThereIs; 827 828 if (parser[NKey::kCaseSensitive].ThereIs) 829 g_CaseSensitive = (parser[NKey::kCaseSensitive].PostCharIndex < 0); 830 831 NRecursedType::EEnum recursedType; 832 if (parser[NKey::kRecursed].ThereIs) 833 recursedType = GetRecursedTypeFromIndex(parser[NKey::kRecursed].PostCharIndex); 834 else 835 recursedType = NRecursedType::kNonRecursed; 836 837 g_CodePage = FindCharset(parser, NKey::kConsoleCharSet, -1); 838 UINT codePage = FindCharset(parser, NKey::kListfileCharSet, CP_UTF8); 839 840 bool thereAreSwitchIncludes = false; 841 if (parser[NKey::kInclude].ThereIs) 842 { 843 thereAreSwitchIncludes = true; 844 AddSwitchWildCardsToCensor(options.WildcardCensor, 845 parser[NKey::kInclude].PostStrings, true, recursedType, codePage); 846 } 847 if (parser[NKey::kExclude].ThereIs) 848 AddSwitchWildCardsToCensor(options.WildcardCensor, 849 parser[NKey::kExclude].PostStrings, false, recursedType, codePage); 850 851 int curCommandIndex = kCommandIndex + 1; 852 bool thereIsArchiveName = !parser[NKey::kNoArName].ThereIs && 853 options.Command.CommandType != NCommandType::kBenchmark && 854 options.Command.CommandType != NCommandType::kInfo; 855 856 bool isExtractGroupCommand = options.Command.IsFromExtractGroup(); 857 bool isExtractOrList = isExtractGroupCommand || options.Command.CommandType == NCommandType::kList; 858 859 if (isExtractOrList && options.StdInMode) 860 thereIsArchiveName = false; 861 862 if (thereIsArchiveName) 863 { 864 if (curCommandIndex >= numNonSwitchStrings) 865 ThrowUserErrorException(); 866 options.ArchiveName = nonSwitchStrings[curCommandIndex++]; 867 if (options.ArchiveName.IsEmpty()) 868 ThrowUserErrorException(); 869 } 870 871 AddToCensorFromNonSwitchesStrings( 872 curCommandIndex, options.WildcardCensor, 873 nonSwitchStrings, recursedType, thereAreSwitchIncludes, codePage); 874 875 options.YesToAll = parser[NKey::kYes].ThereIs; 876 877 878 #ifndef _NO_CRYPTO 879 options.PasswordEnabled = parser[NKey::kPassword].ThereIs; 880 if (options.PasswordEnabled) 881 options.Password = parser[NKey::kPassword].PostStrings[0]; 882 #endif 883 884 options.ShowDialog = parser[NKey::kShowDialog].ThereIs; 885 886 if (parser[NKey::kArchiveType].ThereIs) 887 options.ArcType = parser[NKey::kArchiveType].PostStrings[0]; 888 889 if (isExtractOrList) 890 { 891 if (!options.WildcardCensor.AllAreRelative()) 892 ThrowException("Cannot use absolute pathnames for this command"); 893 894 NWildcard::CCensor archiveWildcardCensor; 895 896 if (parser[NKey::kArInclude].ThereIs) 897 AddSwitchWildCardsToCensor(archiveWildcardCensor, 898 parser[NKey::kArInclude].PostStrings, true, NRecursedType::kNonRecursed, codePage); 899 if (parser[NKey::kArExclude].ThereIs) 900 AddSwitchWildCardsToCensor(archiveWildcardCensor, 901 parser[NKey::kArExclude].PostStrings, false, NRecursedType::kNonRecursed, codePage); 902 903 if (thereIsArchiveName) 904 AddNameToCensor(archiveWildcardCensor, options.ArchiveName, true, NRecursedType::kNonRecursed); 905 906 #ifdef _WIN32 907 ConvertToLongNames(archiveWildcardCensor); 908 #endif 909 910 archiveWildcardCensor.ExtendExclude(); 911 912 if (options.StdInMode) 913 { 914 UString arcName = parser[NKey::kStdIn].PostStrings.Front(); 915 options.ArchivePathsSorted.Add(arcName); 916 options.ArchivePathsFullSorted.Add(arcName); 917 } 918 else 919 { 920 EnumerateDirItemsAndSort(archiveWildcardCensor, 921 options.ArchivePathsSorted, 922 options.ArchivePathsFullSorted); 923 } 924 925 if (isExtractGroupCommand) 926 { 927 SetMethodOptions(parser, options.ExtractProperties); 928 if (options.StdOutMode && options.IsStdOutTerminal && options.IsStdErrTerminal) 929 throw kSameTerminalError; 930 if (parser[NKey::kOutputDir].ThereIs) 931 { 932 options.OutputDir = parser[NKey::kOutputDir].PostStrings[0]; 933 NFile::NName::NormalizeDirPathPrefix(options.OutputDir); 934 } 935 936 options.OverwriteMode = NExtract::NOverwriteMode::kAskBefore; 937 if (parser[NKey::kOverwrite].ThereIs) 938 options.OverwriteMode = k_OverwriteModes[parser[NKey::kOverwrite].PostCharIndex]; 939 else if (options.YesToAll) 940 options.OverwriteMode = NExtract::NOverwriteMode::kWithoutPrompt; 941 } 942 } 943 else if (options.Command.IsFromUpdateGroup()) 944 { 945 CUpdateOptions &updateOptions = options.UpdateOptions; 946 947 SetAddCommandOptions(options.Command.CommandType, parser, updateOptions); 948 949 SetMethodOptions(parser, updateOptions.MethodMode.Properties); 950 951 if (parser[NKey::kShareForWrite].ThereIs) 952 updateOptions.OpenShareForWrite = true; 953 954 options.EnablePercents = !parser[NKey::kDisablePercents].ThereIs; 955 956 if (options.EnablePercents) 957 { 958 if ((options.StdOutMode && !options.IsStdErrTerminal) || 959 (!options.StdOutMode && !options.IsStdOutTerminal)) 960 options.EnablePercents = false; 961 } 962 963 updateOptions.EMailMode = parser[NKey::kEmail].ThereIs; 964 if (updateOptions.EMailMode) 965 { 966 updateOptions.EMailAddress = parser[NKey::kEmail].PostStrings.Front(); 967 if (updateOptions.EMailAddress.Length() > 0) 968 if (updateOptions.EMailAddress[0] == L'.') 969 { 970 updateOptions.EMailRemoveAfter = true; 971 updateOptions.EMailAddress.Delete(0); 972 } 973 } 974 975 updateOptions.StdOutMode = options.StdOutMode; 976 updateOptions.StdInMode = options.StdInMode; 977 978 if (updateOptions.StdOutMode && updateOptions.EMailMode) 979 throw "stdout mode and email mode cannot be combined"; 980 if (updateOptions.StdOutMode && options.IsStdOutTerminal) 981 throw kTerminalOutError; 982 if (updateOptions.StdInMode) 983 updateOptions.StdInFileName = parser[NKey::kStdIn].PostStrings.Front(); 984 985 #ifdef _WIN32 986 ConvertToLongNames(options.WildcardCensor); 987 #endif 988 } 989 else if (options.Command.CommandType == NCommandType::kBenchmark) 990 { 991 options.NumThreads = (UInt32)-1; 992 options.DictionarySize = (UInt32)-1; 993 options.NumIterations = 1; 994 if (curCommandIndex < numNonSwitchStrings) 995 { 996 if (!ConvertStringToUInt32(nonSwitchStrings[curCommandIndex++], options.NumIterations)) 997 ThrowUserErrorException(); 998 } 999 for (int i = 0; i < parser[NKey::kProperty].PostStrings.Size(); i++) 1000 { 1001 UString postString = parser[NKey::kProperty].PostStrings[i]; 1002 postString.MakeUpper(); 1003 if (postString.Length() < 2) 1004 ThrowUserErrorException(); 1005 if (postString[0] == 'D') 1006 { 1007 int pos = 1; 1008 if (postString[pos] == '=') 1009 pos++; 1010 UInt32 logSize; 1011 if (!ConvertStringToUInt32((const wchar_t *)postString + pos, logSize)) 1012 ThrowUserErrorException(); 1013 if (logSize > 31) 1014 ThrowUserErrorException(); 1015 options.DictionarySize = 1 << logSize; 1016 } 1017 else if (postString[0] == 'M' && postString[1] == 'T' ) 1018 { 1019 int pos = 2; 1020 if (postString[pos] == '=') 1021 pos++; 1022 if (postString[pos] != 0) 1023 if (!ConvertStringToUInt32((const wchar_t *)postString + pos, options.NumThreads)) 1024 ThrowUserErrorException(); 1025 } 1026 else if (postString[0] == 'M' && postString[1] == '=' ) 1027 { 1028 int pos = 2; 1029 if (postString[pos] != 0) 1030 options.Method = postString.Mid(2); 1031 } 1032 else 1033 ThrowUserErrorException(); 1034 } 1035 } 1036 else if (options.Command.CommandType == NCommandType::kInfo) 1037 { 1038 } 1039 else 1040 ThrowUserErrorException(); 1041 options.WildcardCensor.ExtendExclude(); 1042 } 1043