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