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