Client7z.cpp (25838B)
1 // Client7z.cpp 2 3 #include "StdAfx.h" 4 5 #include <stdio.h> 6 7 #include "../../../Common/MyWindows.h" 8 9 #include "../../../Common/Defs.h" 10 #include "../../../Common/MyInitGuid.h" 11 12 #include "../../../Common/IntToString.h" 13 #include "../../../Common/StringConvert.h" 14 15 #include "../../../Windows/DLL.h" 16 #include "../../../Windows/FileDir.h" 17 #include "../../../Windows/FileFind.h" 18 #include "../../../Windows/FileName.h" 19 #include "../../../Windows/NtCheck.h" 20 #include "../../../Windows/PropVariant.h" 21 #include "../../../Windows/PropVariantConv.h" 22 23 #include "../../Common/FileStreams.h" 24 25 #include "../../Archive/IArchive.h" 26 27 #include "../../IPassword.h" 28 #include "../../../../C/7zVersion.h" 29 30 #ifdef _WIN32 31 HINSTANCE g_hInstance = 0; 32 #endif 33 34 // Tou can find the list of all GUIDs in Guid.txt file. 35 // use another CLSIDs, if you want to support other formats (zip, rar, ...). 36 // {23170F69-40C1-278A-1000-000110070000} 37 38 DEFINE_GUID(CLSID_CFormat7z, 39 0x23170F69, 0x40C1, 0x278A, 0x10, 0x00, 0x00, 0x01, 0x10, 0x07, 0x00, 0x00); 40 DEFINE_GUID(CLSID_CFormatXz, 41 0x23170F69, 0x40C1, 0x278A, 0x10, 0x00, 0x00, 0x01, 0x10, 0x0C, 0x00, 0x00); 42 43 #define CLSID_Format CLSID_CFormat7z 44 // #define CLSID_Format CLSID_CFormatXz 45 46 using namespace NWindows; 47 using namespace NFile; 48 using namespace NDir; 49 50 #define kDllName "7z.dll" 51 52 static const char * const kCopyrightString = 53 "\n" 54 "7-Zip" 55 " (" kDllName " client)" 56 " " MY_VERSION 57 " : " MY_COPYRIGHT_DATE 58 "\n"; 59 60 static const char * const kHelpString = 61 "Usage: 7zcl.exe [a | l | x] archive.7z [fileName ...]\n" 62 "Examples:\n" 63 " 7zcl.exe a archive.7z f1.txt f2.txt : compress two files to archive.7z\n" 64 " 7zcl.exe l archive.7z : List contents of archive.7z\n" 65 " 7zcl.exe x archive.7z : eXtract files from archive.7z\n"; 66 67 68 static void Convert_UString_to_AString(const UString &s, AString &temp) 69 { 70 int codePage = CP_OEMCP; 71 /* 72 int g_CodePage = -1; 73 int codePage = g_CodePage; 74 if (codePage == -1) 75 codePage = CP_OEMCP; 76 if (codePage == CP_UTF8) 77 ConvertUnicodeToUTF8(s, temp); 78 else 79 */ 80 UnicodeStringToMultiByte2(temp, s, (UINT)codePage); 81 } 82 83 static FString CmdStringToFString(const char *s) 84 { 85 return us2fs(GetUnicodeString(s)); 86 } 87 88 static void Print(const char *s) 89 { 90 fputs(s, stdout); 91 } 92 93 static void Print(const AString &s) 94 { 95 Print(s.Ptr()); 96 } 97 98 static void Print(const UString &s) 99 { 100 AString as; 101 Convert_UString_to_AString(s, as); 102 Print(as); 103 } 104 105 static void Print(const wchar_t *s) 106 { 107 Print(UString(s)); 108 } 109 110 static void PrintNewLine() 111 { 112 Print("\n"); 113 } 114 115 static void PrintStringLn(const char *s) 116 { 117 Print(s); 118 PrintNewLine(); 119 } 120 121 static void PrintError(const char *message) 122 { 123 Print("Error: "); 124 PrintNewLine(); 125 Print(message); 126 PrintNewLine(); 127 } 128 129 static void PrintError(const char *message, const FString &name) 130 { 131 PrintError(message); 132 Print(name); 133 } 134 135 136 static HRESULT IsArchiveItemProp(IInArchive *archive, UInt32 index, PROPID propID, bool &result) 137 { 138 NCOM::CPropVariant prop; 139 RINOK(archive->GetProperty(index, propID, &prop)); 140 if (prop.vt == VT_BOOL) 141 result = VARIANT_BOOLToBool(prop.boolVal); 142 else if (prop.vt == VT_EMPTY) 143 result = false; 144 else 145 return E_FAIL; 146 return S_OK; 147 } 148 149 static HRESULT IsArchiveItemFolder(IInArchive *archive, UInt32 index, bool &result) 150 { 151 return IsArchiveItemProp(archive, index, kpidIsDir, result); 152 } 153 154 155 static const wchar_t * const kEmptyFileAlias = L"[Content]"; 156 157 158 ////////////////////////////////////////////////////////////// 159 // Archive Open callback class 160 161 162 class CArchiveOpenCallback: 163 public IArchiveOpenCallback, 164 public ICryptoGetTextPassword, 165 public CMyUnknownImp 166 { 167 public: 168 MY_UNKNOWN_IMP1(ICryptoGetTextPassword) 169 170 STDMETHOD(SetTotal)(const UInt64 *files, const UInt64 *bytes); 171 STDMETHOD(SetCompleted)(const UInt64 *files, const UInt64 *bytes); 172 173 STDMETHOD(CryptoGetTextPassword)(BSTR *password); 174 175 bool PasswordIsDefined; 176 UString Password; 177 178 CArchiveOpenCallback() : PasswordIsDefined(false) {} 179 }; 180 181 STDMETHODIMP CArchiveOpenCallback::SetTotal(const UInt64 * /* files */, const UInt64 * /* bytes */) 182 { 183 return S_OK; 184 } 185 186 STDMETHODIMP CArchiveOpenCallback::SetCompleted(const UInt64 * /* files */, const UInt64 * /* bytes */) 187 { 188 return S_OK; 189 } 190 191 STDMETHODIMP CArchiveOpenCallback::CryptoGetTextPassword(BSTR *password) 192 { 193 if (!PasswordIsDefined) 194 { 195 // You can ask real password here from user 196 // Password = GetPassword(OutStream); 197 // PasswordIsDefined = true; 198 PrintError("Password is not defined"); 199 return E_ABORT; 200 } 201 return StringToBstr(Password, password); 202 } 203 204 205 206 static const char * const kIncorrectCommand = "incorrect command"; 207 208 ////////////////////////////////////////////////////////////// 209 // Archive Extracting callback class 210 211 static const char * const kTestingString = "Testing "; 212 static const char * const kExtractingString = "Extracting "; 213 static const char * const kSkippingString = "Skipping "; 214 215 static const char * const kUnsupportedMethod = "Unsupported Method"; 216 static const char * const kCRCFailed = "CRC Failed"; 217 static const char * const kDataError = "Data Error"; 218 static const char * const kUnavailableData = "Unavailable data"; 219 static const char * const kUnexpectedEnd = "Unexpected end of data"; 220 static const char * const kDataAfterEnd = "There are some data after the end of the payload data"; 221 static const char * const kIsNotArc = "Is not archive"; 222 static const char * const kHeadersError = "Headers Error"; 223 224 225 class CArchiveExtractCallback: 226 public IArchiveExtractCallback, 227 public ICryptoGetTextPassword, 228 public CMyUnknownImp 229 { 230 public: 231 MY_UNKNOWN_IMP1(ICryptoGetTextPassword) 232 233 // IProgress 234 STDMETHOD(SetTotal)(UInt64 size); 235 STDMETHOD(SetCompleted)(const UInt64 *completeValue); 236 237 // IArchiveExtractCallback 238 STDMETHOD(GetStream)(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode); 239 STDMETHOD(PrepareOperation)(Int32 askExtractMode); 240 STDMETHOD(SetOperationResult)(Int32 resultEOperationResult); 241 242 // ICryptoGetTextPassword 243 STDMETHOD(CryptoGetTextPassword)(BSTR *aPassword); 244 245 private: 246 CMyComPtr<IInArchive> _archiveHandler; 247 FString _directoryPath; // Output directory 248 UString _filePath; // name inside arcvhive 249 FString _diskFilePath; // full path to file on disk 250 bool _extractMode; 251 struct CProcessedFileInfo 252 { 253 FILETIME MTime; 254 UInt32 Attrib; 255 bool isDir; 256 bool AttribDefined; 257 bool MTimeDefined; 258 } _processedFileInfo; 259 260 COutFileStream *_outFileStreamSpec; 261 CMyComPtr<ISequentialOutStream> _outFileStream; 262 263 public: 264 void Init(IInArchive *archiveHandler, const FString &directoryPath); 265 266 UInt64 NumErrors; 267 bool PasswordIsDefined; 268 UString Password; 269 270 CArchiveExtractCallback() : PasswordIsDefined(false) {} 271 }; 272 273 void CArchiveExtractCallback::Init(IInArchive *archiveHandler, const FString &directoryPath) 274 { 275 NumErrors = 0; 276 _archiveHandler = archiveHandler; 277 _directoryPath = directoryPath; 278 NName::NormalizeDirPathPrefix(_directoryPath); 279 } 280 281 STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 /* size */) 282 { 283 return S_OK; 284 } 285 286 STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 * /* completeValue */) 287 { 288 return S_OK; 289 } 290 291 STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, 292 ISequentialOutStream **outStream, Int32 askExtractMode) 293 { 294 *outStream = 0; 295 _outFileStream.Release(); 296 297 { 298 // Get Name 299 NCOM::CPropVariant prop; 300 RINOK(_archiveHandler->GetProperty(index, kpidPath, &prop)); 301 302 UString fullPath; 303 if (prop.vt == VT_EMPTY) 304 fullPath = kEmptyFileAlias; 305 else 306 { 307 if (prop.vt != VT_BSTR) 308 return E_FAIL; 309 fullPath = prop.bstrVal; 310 } 311 _filePath = fullPath; 312 } 313 314 if (askExtractMode != NArchive::NExtract::NAskMode::kExtract) 315 return S_OK; 316 317 { 318 // Get Attrib 319 NCOM::CPropVariant prop; 320 RINOK(_archiveHandler->GetProperty(index, kpidAttrib, &prop)); 321 if (prop.vt == VT_EMPTY) 322 { 323 _processedFileInfo.Attrib = 0; 324 _processedFileInfo.AttribDefined = false; 325 } 326 else 327 { 328 if (prop.vt != VT_UI4) 329 return E_FAIL; 330 _processedFileInfo.Attrib = prop.ulVal; 331 _processedFileInfo.AttribDefined = true; 332 } 333 } 334 335 RINOK(IsArchiveItemFolder(_archiveHandler, index, _processedFileInfo.isDir)); 336 337 { 338 // Get Modified Time 339 NCOM::CPropVariant prop; 340 RINOK(_archiveHandler->GetProperty(index, kpidMTime, &prop)); 341 _processedFileInfo.MTimeDefined = false; 342 switch (prop.vt) 343 { 344 case VT_EMPTY: 345 // _processedFileInfo.MTime = _utcMTimeDefault; 346 break; 347 case VT_FILETIME: 348 _processedFileInfo.MTime = prop.filetime; 349 _processedFileInfo.MTimeDefined = true; 350 break; 351 default: 352 return E_FAIL; 353 } 354 355 } 356 { 357 // Get Size 358 NCOM::CPropVariant prop; 359 RINOK(_archiveHandler->GetProperty(index, kpidSize, &prop)); 360 UInt64 newFileSize; 361 /* bool newFileSizeDefined = */ ConvertPropVariantToUInt64(prop, newFileSize); 362 } 363 364 365 { 366 // Create folders for file 367 int slashPos = _filePath.ReverseFind_PathSepar(); 368 if (slashPos >= 0) 369 CreateComplexDir(_directoryPath + us2fs(_filePath.Left(slashPos))); 370 } 371 372 FString fullProcessedPath = _directoryPath + us2fs(_filePath); 373 _diskFilePath = fullProcessedPath; 374 375 if (_processedFileInfo.isDir) 376 { 377 CreateComplexDir(fullProcessedPath); 378 } 379 else 380 { 381 NFind::CFileInfo fi; 382 if (fi.Find(fullProcessedPath)) 383 { 384 if (!DeleteFileAlways(fullProcessedPath)) 385 { 386 PrintError("Can not delete output file", fullProcessedPath); 387 return E_ABORT; 388 } 389 } 390 391 _outFileStreamSpec = new COutFileStream; 392 CMyComPtr<ISequentialOutStream> outStreamLoc(_outFileStreamSpec); 393 if (!_outFileStreamSpec->Open(fullProcessedPath, CREATE_ALWAYS)) 394 { 395 PrintError("Can not open output file", fullProcessedPath); 396 return E_ABORT; 397 } 398 _outFileStream = outStreamLoc; 399 *outStream = outStreamLoc.Detach(); 400 } 401 return S_OK; 402 } 403 404 STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode) 405 { 406 _extractMode = false; 407 switch (askExtractMode) 408 { 409 case NArchive::NExtract::NAskMode::kExtract: _extractMode = true; break; 410 }; 411 switch (askExtractMode) 412 { 413 case NArchive::NExtract::NAskMode::kExtract: Print(kExtractingString); break; 414 case NArchive::NExtract::NAskMode::kTest: Print(kTestingString); break; 415 case NArchive::NExtract::NAskMode::kSkip: Print(kSkippingString); break; 416 }; 417 Print(_filePath); 418 return S_OK; 419 } 420 421 STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult) 422 { 423 switch (operationResult) 424 { 425 case NArchive::NExtract::NOperationResult::kOK: 426 break; 427 default: 428 { 429 NumErrors++; 430 Print(" : "); 431 const char *s = NULL; 432 switch (operationResult) 433 { 434 case NArchive::NExtract::NOperationResult::kUnsupportedMethod: 435 s = kUnsupportedMethod; 436 break; 437 case NArchive::NExtract::NOperationResult::kCRCError: 438 s = kCRCFailed; 439 break; 440 case NArchive::NExtract::NOperationResult::kDataError: 441 s = kDataError; 442 break; 443 case NArchive::NExtract::NOperationResult::kUnavailable: 444 s = kUnavailableData; 445 break; 446 case NArchive::NExtract::NOperationResult::kUnexpectedEnd: 447 s = kUnexpectedEnd; 448 break; 449 case NArchive::NExtract::NOperationResult::kDataAfterEnd: 450 s = kDataAfterEnd; 451 break; 452 case NArchive::NExtract::NOperationResult::kIsNotArc: 453 s = kIsNotArc; 454 break; 455 case NArchive::NExtract::NOperationResult::kHeadersError: 456 s = kHeadersError; 457 break; 458 } 459 if (s) 460 { 461 Print("Error : "); 462 Print(s); 463 } 464 else 465 { 466 char temp[16]; 467 ConvertUInt32ToString(operationResult, temp); 468 Print("Error #"); 469 Print(temp); 470 } 471 } 472 } 473 474 if (_outFileStream) 475 { 476 if (_processedFileInfo.MTimeDefined) 477 _outFileStreamSpec->SetMTime(&_processedFileInfo.MTime); 478 RINOK(_outFileStreamSpec->Close()); 479 } 480 _outFileStream.Release(); 481 if (_extractMode && _processedFileInfo.AttribDefined) 482 SetFileAttrib_PosixHighDetect(_diskFilePath, _processedFileInfo.Attrib); 483 PrintNewLine(); 484 return S_OK; 485 } 486 487 488 STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password) 489 { 490 if (!PasswordIsDefined) 491 { 492 // You can ask real password here from user 493 // Password = GetPassword(OutStream); 494 // PasswordIsDefined = true; 495 PrintError("Password is not defined"); 496 return E_ABORT; 497 } 498 return StringToBstr(Password, password); 499 } 500 501 502 503 ////////////////////////////////////////////////////////////// 504 // Archive Creating callback class 505 506 struct CDirItem 507 { 508 UInt64 Size; 509 FILETIME CTime; 510 FILETIME ATime; 511 FILETIME MTime; 512 UString Name; 513 FString FullPath; 514 UInt32 Attrib; 515 516 bool isDir() const { return (Attrib & FILE_ATTRIBUTE_DIRECTORY) != 0 ; } 517 }; 518 519 class CArchiveUpdateCallback: 520 public IArchiveUpdateCallback2, 521 public ICryptoGetTextPassword2, 522 public CMyUnknownImp 523 { 524 public: 525 MY_UNKNOWN_IMP2(IArchiveUpdateCallback2, ICryptoGetTextPassword2) 526 527 // IProgress 528 STDMETHOD(SetTotal)(UInt64 size); 529 STDMETHOD(SetCompleted)(const UInt64 *completeValue); 530 531 // IUpdateCallback2 532 STDMETHOD(GetUpdateItemInfo)(UInt32 index, 533 Int32 *newData, Int32 *newProperties, UInt32 *indexInArchive); 534 STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value); 535 STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **inStream); 536 STDMETHOD(SetOperationResult)(Int32 operationResult); 537 STDMETHOD(GetVolumeSize)(UInt32 index, UInt64 *size); 538 STDMETHOD(GetVolumeStream)(UInt32 index, ISequentialOutStream **volumeStream); 539 540 STDMETHOD(CryptoGetTextPassword2)(Int32 *passwordIsDefined, BSTR *password); 541 542 public: 543 CRecordVector<UInt64> VolumesSizes; 544 UString VolName; 545 UString VolExt; 546 547 FString DirPrefix; 548 const CObjectVector<CDirItem> *DirItems; 549 550 bool PasswordIsDefined; 551 UString Password; 552 bool AskPassword; 553 554 bool m_NeedBeClosed; 555 556 FStringVector FailedFiles; 557 CRecordVector<HRESULT> FailedCodes; 558 559 CArchiveUpdateCallback(): PasswordIsDefined(false), AskPassword(false), DirItems(0) {}; 560 561 ~CArchiveUpdateCallback() { Finilize(); } 562 HRESULT Finilize(); 563 564 void Init(const CObjectVector<CDirItem> *dirItems) 565 { 566 DirItems = dirItems; 567 m_NeedBeClosed = false; 568 FailedFiles.Clear(); 569 FailedCodes.Clear(); 570 } 571 }; 572 573 STDMETHODIMP CArchiveUpdateCallback::SetTotal(UInt64 /* size */) 574 { 575 return S_OK; 576 } 577 578 STDMETHODIMP CArchiveUpdateCallback::SetCompleted(const UInt64 * /* completeValue */) 579 { 580 return S_OK; 581 } 582 583 STDMETHODIMP CArchiveUpdateCallback::GetUpdateItemInfo(UInt32 /* index */, 584 Int32 *newData, Int32 *newProperties, UInt32 *indexInArchive) 585 { 586 if (newData) 587 *newData = BoolToInt(true); 588 if (newProperties) 589 *newProperties = BoolToInt(true); 590 if (indexInArchive) 591 *indexInArchive = (UInt32)(Int32)-1; 592 return S_OK; 593 } 594 595 STDMETHODIMP CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) 596 { 597 NCOM::CPropVariant prop; 598 599 if (propID == kpidIsAnti) 600 { 601 prop = false; 602 prop.Detach(value); 603 return S_OK; 604 } 605 606 { 607 const CDirItem &dirItem = (*DirItems)[index]; 608 switch (propID) 609 { 610 case kpidPath: prop = dirItem.Name; break; 611 case kpidIsDir: prop = dirItem.isDir(); break; 612 case kpidSize: prop = dirItem.Size; break; 613 case kpidAttrib: prop = dirItem.Attrib; break; 614 case kpidCTime: prop = dirItem.CTime; break; 615 case kpidATime: prop = dirItem.ATime; break; 616 case kpidMTime: prop = dirItem.MTime; break; 617 } 618 } 619 prop.Detach(value); 620 return S_OK; 621 } 622 623 HRESULT CArchiveUpdateCallback::Finilize() 624 { 625 if (m_NeedBeClosed) 626 { 627 PrintNewLine(); 628 m_NeedBeClosed = false; 629 } 630 return S_OK; 631 } 632 633 static void GetStream2(const wchar_t *name) 634 { 635 Print("Compressing "); 636 if (name[0] == 0) 637 name = kEmptyFileAlias; 638 Print(name); 639 } 640 641 STDMETHODIMP CArchiveUpdateCallback::GetStream(UInt32 index, ISequentialInStream **inStream) 642 { 643 RINOK(Finilize()); 644 645 const CDirItem &dirItem = (*DirItems)[index]; 646 GetStream2(dirItem.Name); 647 648 if (dirItem.isDir()) 649 return S_OK; 650 651 { 652 CInFileStream *inStreamSpec = new CInFileStream; 653 CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec); 654 FString path = DirPrefix + dirItem.FullPath; 655 if (!inStreamSpec->Open(path)) 656 { 657 DWORD sysError = ::GetLastError(); 658 FailedCodes.Add(sysError); 659 FailedFiles.Add(path); 660 // if (systemError == ERROR_SHARING_VIOLATION) 661 { 662 PrintNewLine(); 663 PrintError("WARNING: can't open file"); 664 // Print(NError::MyFormatMessageW(systemError)); 665 return S_FALSE; 666 } 667 // return sysError; 668 } 669 *inStream = inStreamLoc.Detach(); 670 } 671 return S_OK; 672 } 673 674 STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 /* operationResult */) 675 { 676 m_NeedBeClosed = true; 677 return S_OK; 678 } 679 680 STDMETHODIMP CArchiveUpdateCallback::GetVolumeSize(UInt32 index, UInt64 *size) 681 { 682 if (VolumesSizes.Size() == 0) 683 return S_FALSE; 684 if (index >= (UInt32)VolumesSizes.Size()) 685 index = VolumesSizes.Size() - 1; 686 *size = VolumesSizes[index]; 687 return S_OK; 688 } 689 690 STDMETHODIMP CArchiveUpdateCallback::GetVolumeStream(UInt32 index, ISequentialOutStream **volumeStream) 691 { 692 wchar_t temp[16]; 693 ConvertUInt32ToString(index + 1, temp); 694 UString res = temp; 695 while (res.Len() < 2) 696 res.InsertAtFront(L'0'); 697 UString fileName = VolName; 698 fileName += '.'; 699 fileName += res; 700 fileName += VolExt; 701 COutFileStream *streamSpec = new COutFileStream; 702 CMyComPtr<ISequentialOutStream> streamLoc(streamSpec); 703 if (!streamSpec->Create(us2fs(fileName), false)) 704 return ::GetLastError(); 705 *volumeStream = streamLoc.Detach(); 706 return S_OK; 707 } 708 709 STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password) 710 { 711 if (!PasswordIsDefined) 712 { 713 if (AskPassword) 714 { 715 // You can ask real password here from user 716 // Password = GetPassword(OutStream); 717 // PasswordIsDefined = true; 718 PrintError("Password is not defined"); 719 return E_ABORT; 720 } 721 } 722 *passwordIsDefined = BoolToInt(PasswordIsDefined); 723 return StringToBstr(Password, password); 724 } 725 726 727 // Main function 728 729 #define NT_CHECK_FAIL_ACTION PrintError("Unsupported Windows version"); return 1; 730 731 int MY_CDECL main(int numArgs, const char *args[]) 732 { 733 NT_CHECK 734 735 PrintStringLn(kCopyrightString); 736 737 if (numArgs < 2) 738 { 739 PrintStringLn(kHelpString); 740 return 0; 741 } 742 743 if (numArgs < 3) 744 { 745 PrintError(kIncorrectCommand); 746 return 1; 747 } 748 749 750 NDLL::CLibrary lib; 751 if (!lib.Load(NDLL::GetModuleDirPrefix() + FTEXT(kDllName))) 752 { 753 PrintError("Can not load 7-zip library"); 754 return 1; 755 } 756 757 Func_CreateObject createObjectFunc = (Func_CreateObject)lib.GetProc("CreateObject"); 758 if (!createObjectFunc) 759 { 760 PrintError("Can not get CreateObject"); 761 return 1; 762 } 763 764 char c; 765 { 766 AString command (args[1]); 767 if (command.Len() != 1) 768 { 769 PrintError(kIncorrectCommand); 770 return 1; 771 } 772 c = (char)MyCharLower_Ascii(command[0]); 773 } 774 775 FString archiveName = CmdStringToFString(args[2]); 776 777 if (c == 'a') 778 { 779 // create archive command 780 if (numArgs < 4) 781 { 782 PrintError(kIncorrectCommand); 783 return 1; 784 } 785 CObjectVector<CDirItem> dirItems; 786 { 787 int i; 788 for (i = 3; i < numArgs; i++) 789 { 790 CDirItem di; 791 FString name = CmdStringToFString(args[i]); 792 793 NFind::CFileInfo fi; 794 if (!fi.Find(name)) 795 { 796 PrintError("Can't find file", name); 797 return 1; 798 } 799 800 di.Attrib = fi.Attrib; 801 di.Size = fi.Size; 802 di.CTime = fi.CTime; 803 di.ATime = fi.ATime; 804 di.MTime = fi.MTime; 805 di.Name = fs2us(name); 806 di.FullPath = name; 807 dirItems.Add(di); 808 } 809 } 810 811 COutFileStream *outFileStreamSpec = new COutFileStream; 812 CMyComPtr<IOutStream> outFileStream = outFileStreamSpec; 813 if (!outFileStreamSpec->Create(archiveName, false)) 814 { 815 PrintError("can't create archive file"); 816 return 1; 817 } 818 819 CMyComPtr<IOutArchive> outArchive; 820 if (createObjectFunc(&CLSID_Format, &IID_IOutArchive, (void **)&outArchive) != S_OK) 821 { 822 PrintError("Can not get class object"); 823 return 1; 824 } 825 826 CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback; 827 CMyComPtr<IArchiveUpdateCallback2> updateCallback(updateCallbackSpec); 828 updateCallbackSpec->Init(&dirItems); 829 // updateCallbackSpec->PasswordIsDefined = true; 830 // updateCallbackSpec->Password = L"1"; 831 832 /* 833 { 834 const wchar_t *names[] = 835 { 836 L"s", 837 L"x" 838 }; 839 const unsigned kNumProps = ARRAY_SIZE(names); 840 NCOM::CPropVariant values[kNumProps] = 841 { 842 false, // solid mode OFF 843 (UInt32)9 // compression level = 9 - ultra 844 }; 845 CMyComPtr<ISetProperties> setProperties; 846 outArchive->QueryInterface(IID_ISetProperties, (void **)&setProperties); 847 if (!setProperties) 848 { 849 PrintError("ISetProperties unsupported"); 850 return 1; 851 } 852 RINOK(setProperties->SetProperties(names, values, kNumProps)); 853 } 854 */ 855 856 HRESULT result = outArchive->UpdateItems(outFileStream, dirItems.Size(), updateCallback); 857 858 updateCallbackSpec->Finilize(); 859 860 if (result != S_OK) 861 { 862 PrintError("Update Error"); 863 return 1; 864 } 865 866 FOR_VECTOR (i, updateCallbackSpec->FailedFiles) 867 { 868 PrintNewLine(); 869 PrintError("Error for file", updateCallbackSpec->FailedFiles[i]); 870 } 871 872 if (updateCallbackSpec->FailedFiles.Size() != 0) 873 return 1; 874 } 875 else 876 { 877 if (numArgs != 3) 878 { 879 PrintError(kIncorrectCommand); 880 return 1; 881 } 882 883 bool listCommand; 884 885 if (c == 'l') 886 listCommand = true; 887 else if (c == 'x') 888 listCommand = false; 889 else 890 { 891 PrintError(kIncorrectCommand); 892 return 1; 893 } 894 895 CMyComPtr<IInArchive> archive; 896 if (createObjectFunc(&CLSID_Format, &IID_IInArchive, (void **)&archive) != S_OK) 897 { 898 PrintError("Can not get class object"); 899 return 1; 900 } 901 902 CInFileStream *fileSpec = new CInFileStream; 903 CMyComPtr<IInStream> file = fileSpec; 904 905 if (!fileSpec->Open(archiveName)) 906 { 907 PrintError("Can not open archive file", archiveName); 908 return 1; 909 } 910 911 { 912 CArchiveOpenCallback *openCallbackSpec = new CArchiveOpenCallback; 913 CMyComPtr<IArchiveOpenCallback> openCallback(openCallbackSpec); 914 openCallbackSpec->PasswordIsDefined = false; 915 // openCallbackSpec->PasswordIsDefined = true; 916 // openCallbackSpec->Password = L"1"; 917 918 const UInt64 scanSize = 1 << 23; 919 if (archive->Open(file, &scanSize, openCallback) != S_OK) 920 { 921 PrintError("Can not open file as archive", archiveName); 922 return 1; 923 } 924 } 925 926 if (listCommand) 927 { 928 // List command 929 UInt32 numItems = 0; 930 archive->GetNumberOfItems(&numItems); 931 for (UInt32 i = 0; i < numItems; i++) 932 { 933 { 934 // Get uncompressed size of file 935 NCOM::CPropVariant prop; 936 archive->GetProperty(i, kpidSize, &prop); 937 char s[32]; 938 ConvertPropVariantToShortString(prop, s); 939 Print(s); 940 Print(" "); 941 } 942 { 943 // Get name of file 944 NCOM::CPropVariant prop; 945 archive->GetProperty(i, kpidPath, &prop); 946 if (prop.vt == VT_BSTR) 947 Print(prop.bstrVal); 948 else if (prop.vt != VT_EMPTY) 949 Print("ERROR!"); 950 } 951 PrintNewLine(); 952 } 953 } 954 else 955 { 956 // Extract command 957 CArchiveExtractCallback *extractCallbackSpec = new CArchiveExtractCallback; 958 CMyComPtr<IArchiveExtractCallback> extractCallback(extractCallbackSpec); 959 extractCallbackSpec->Init(archive, FString()); // second parameter is output folder path 960 extractCallbackSpec->PasswordIsDefined = false; 961 // extractCallbackSpec->PasswordIsDefined = true; 962 // extractCallbackSpec->Password = "1"; 963 964 /* 965 const wchar_t *names[] = 966 { 967 L"mt", 968 L"mtf" 969 }; 970 const unsigned kNumProps = sizeof(names) / sizeof(names[0]); 971 NCOM::CPropVariant values[kNumProps] = 972 { 973 (UInt32)1, 974 false 975 }; 976 CMyComPtr<ISetProperties> setProperties; 977 archive->QueryInterface(IID_ISetProperties, (void **)&setProperties); 978 if (setProperties) 979 setProperties->SetProperties(names, values, kNumProps); 980 */ 981 982 HRESULT result = archive->Extract(NULL, (UInt32)(Int32)(-1), false, extractCallback); 983 984 if (result != S_OK) 985 { 986 PrintError("Extract Error"); 987 return 1; 988 } 989 } 990 } 991 992 return 0; 993 }