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