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