misc/libphysfs/lzma/CPP/7zip/UI/Client7z/Client7z.cpp
changeset 12213 bb5522e88ab2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/libphysfs/lzma/CPP/7zip/UI/Client7z/Client7z.cpp	Mon Apr 10 12:06:43 2017 -0400
@@ -0,0 +1,880 @@
+// Client7z.cpp
+
+#include "StdAfx.h"
+
+#include "Common/MyInitGuid.h"
+#include "Common/StringConvert.h"
+#include "Common/IntToString.h"
+
+#include "Windows/PropVariant.h"
+#include "Windows/PropVariantConversions.h"
+#include "Windows/DLL.h"
+#include "Windows/FileDir.h"
+#include "Windows/FileName.h"
+#include "Windows/FileFind.h"
+
+#include "../../Common/FileStreams.h"
+#include "../../Archive/IArchive.h"
+#include "../../IPassword.h"
+#include "../../MyVersion.h"
+
+
+// {23170F69-40C1-278A-1000-000110070000}
+DEFINE_GUID(CLSID_CFormat7z, 
+  0x23170F69, 0x40C1, 0x278A, 0x10, 0x00, 0x00, 0x01, 0x10, 0x07, 0x00, 0x00);
+
+using namespace NWindows;
+
+#define kDllName "7z.dll"
+
+static const char *kCopyrightString = MY_7ZIP_VERSION
+" ("  kDllName " client) "  
+MY_COPYRIGHT " " MY_DATE;
+
+static const char *kHelpString = 
+"Usage: Client7z.exe [a | l | x ] archive.7z [fileName ...]\n"
+"Examples:\n"
+"  Client7z.exe a archive.7z f1.txt f2.txt  : compress two files to archive.7z\n"
+"  Client7z.exe l archive.7z   : List contents of archive.7z\n"
+"  Client7z.exe x archive.7z   : eXtract files from archive.7z\n";
+
+
+typedef UINT32 (WINAPI * CreateObjectFunc)(
+    const GUID *clsID, 
+    const GUID *interfaceID, 
+    void **outObject);
+
+#ifdef _WIN32
+#ifndef _UNICODE
+bool g_IsNT = false;
+static inline bool IsItWindowsNT()
+{
+  OSVERSIONINFO versionInfo;
+  versionInfo.dwOSVersionInfoSize = sizeof(versionInfo);
+  if (!::GetVersionEx(&versionInfo)) 
+    return false;
+  return (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT);
+}
+#endif
+#endif
+
+void PrintString(const UString &s)
+{
+  printf("%s", (LPCSTR)GetOemString(s));
+}
+
+void PrintString(const AString &s)
+{
+  printf("%s", (LPCSTR)s);
+}
+
+void PrintNewLine()
+{
+  PrintString("\n");
+}
+
+void PrintStringLn(const AString &s)
+{
+  PrintString(s);
+  PrintNewLine();
+}
+
+void PrintError(const AString &s)
+{
+  PrintNewLine();
+  PrintString(s);
+  PrintNewLine();
+}
+
+static HRESULT IsArchiveItemProp(IInArchive *archive, UInt32 index, PROPID propID, bool &result)
+{
+  NCOM::CPropVariant prop;
+  RINOK(archive->GetProperty(index, propID, &prop));
+  if(prop.vt == VT_BOOL)
+    result = VARIANT_BOOLToBool(prop.boolVal);
+  else if (prop.vt == VT_EMPTY)
+    result = false;
+  else
+    return E_FAIL;
+  return S_OK;
+}
+
+static HRESULT IsArchiveItemFolder(IInArchive *archive, UInt32 index, bool &result)
+{
+  return IsArchiveItemProp(archive, index, kpidIsFolder, result);
+}
+
+
+static const wchar_t *kEmptyFileAlias = L"[Content]";
+
+
+//////////////////////////////////////////////////////////////
+// Archive Open callback class
+
+
+class CArchiveOpenCallback: 
+  public IArchiveOpenCallback,
+  public ICryptoGetTextPassword,
+  public CMyUnknownImp
+{
+public:
+  MY_UNKNOWN_IMP1(ICryptoGetTextPassword)
+
+  STDMETHOD(SetTotal)(const UInt64 *files, const UInt64 *bytes);
+  STDMETHOD(SetCompleted)(const UInt64 *files, const UInt64 *bytes);
+
+  STDMETHOD(CryptoGetTextPassword)(BSTR *password);
+
+  bool PasswordIsDefined;
+  UString Password;
+
+  CArchiveOpenCallback() : PasswordIsDefined(false) {}
+};
+
+STDMETHODIMP CArchiveOpenCallback::SetTotal(const UInt64 * /* files */, const UInt64 * /* bytes */)
+{
+  return S_OK;
+}
+
+STDMETHODIMP CArchiveOpenCallback::SetCompleted(const UInt64 * /* files */, const UInt64 * /* bytes */)
+{
+  return S_OK;
+}
+  
+STDMETHODIMP CArchiveOpenCallback::CryptoGetTextPassword(BSTR *password)
+{
+  if (!PasswordIsDefined)
+  {
+    // You can ask real password here from user
+    // Password = GetPassword(OutStream); 
+    // PasswordIsDefined = true;
+    PrintError("Password is not defined");
+    return E_ABORT;
+  }
+  CMyComBSTR tempName(Password);
+  *password = tempName.Detach();
+  return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////
+// Archive Extracting callback class
+
+static const wchar_t *kCantDeleteOutputFile = L"ERROR: Can not delete output file ";
+
+static const char *kTestingString    =  "Testing     ";
+static const char *kExtractingString =  "Extracting  ";
+static const char *kSkippingString   =  "Skipping    ";
+
+static const char *kUnsupportedMethod = "Unsupported Method";
+static const char *kCRCFailed = "CRC Failed";
+static const char *kDataError = "Data Error";
+static const char *kUnknownError = "Unknown Error";
+
+class CArchiveExtractCallback: 
+  public IArchiveExtractCallback,
+  public ICryptoGetTextPassword,
+  public CMyUnknownImp
+{
+public:
+  MY_UNKNOWN_IMP1(ICryptoGetTextPassword)
+
+  // IProgress
+  STDMETHOD(SetTotal)(UInt64 size);
+  STDMETHOD(SetCompleted)(const UInt64 *completeValue);
+
+  // IArchiveExtractCallback
+  STDMETHOD(GetStream)(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode);
+  STDMETHOD(PrepareOperation)(Int32 askExtractMode);
+  STDMETHOD(SetOperationResult)(Int32 resultEOperationResult);
+
+  // ICryptoGetTextPassword
+  STDMETHOD(CryptoGetTextPassword)(BSTR *aPassword);
+
+private:
+  CMyComPtr<IInArchive> _archiveHandler;
+  UString _directoryPath;  // Output directory
+  UString _filePath;       // name inside arcvhive
+  UString _diskFilePath;   // full path to file on disk
+  bool _extractMode;
+  struct CProcessedFileInfo
+  {
+    FILETIME UTCLastWriteTime;
+    UInt32 Attributes;
+    bool IsDirectory;
+    bool AttributesAreDefined;
+    bool UTCLastWriteTimeIsDefined;
+  } _processedFileInfo;
+
+  COutFileStream *_outFileStreamSpec;
+  CMyComPtr<ISequentialOutStream> _outFileStream;
+
+public:
+  void Init(IInArchive *archiveHandler, const UString &directoryPath);
+
+  UInt64 NumErrors;
+  bool PasswordIsDefined;
+  UString Password;
+
+  CArchiveExtractCallback() : PasswordIsDefined(false) {}
+};
+
+void CArchiveExtractCallback::Init(IInArchive *archiveHandler, const UString &directoryPath)
+{
+  NumErrors = 0;
+  _archiveHandler = archiveHandler;
+  _directoryPath = directoryPath;
+  NFile::NName::NormalizeDirPathPrefix(_directoryPath);
+}
+
+STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 /* size */)
+{
+  return S_OK;
+}
+
+STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 * /* completeValue */)
+{
+  return S_OK;
+}
+
+STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, 
+    ISequentialOutStream **outStream, Int32 askExtractMode)
+{
+  *outStream = 0;
+  _outFileStream.Release();
+
+  {
+    // Get Name
+    NCOM::CPropVariant propVariant;
+    RINOK(_archiveHandler->GetProperty(index, kpidPath, &propVariant));
+    
+    UString fullPath;
+    if(propVariant.vt == VT_EMPTY)
+      fullPath = kEmptyFileAlias;
+    else 
+    {
+      if(propVariant.vt != VT_BSTR)
+        return E_FAIL;
+      fullPath = propVariant.bstrVal;
+    }
+    _filePath = fullPath;
+  }
+
+  if (askExtractMode != NArchive::NExtract::NAskMode::kExtract)
+    return S_OK;
+
+  {
+    // Get Attributes
+    NCOM::CPropVariant propVariant;
+    RINOK(_archiveHandler->GetProperty(index, kpidAttributes, &propVariant));
+    if (propVariant.vt == VT_EMPTY)
+    {
+      _processedFileInfo.Attributes = 0;
+      _processedFileInfo.AttributesAreDefined = false;
+    }
+    else
+    {
+      if (propVariant.vt != VT_UI4)
+        throw "incorrect item";
+      _processedFileInfo.Attributes = propVariant.ulVal;
+      _processedFileInfo.AttributesAreDefined = true;
+    }
+  }
+
+  RINOK(IsArchiveItemFolder(_archiveHandler, index, _processedFileInfo.IsDirectory));
+
+  {
+    // Get Modified Time
+    NCOM::CPropVariant propVariant;
+    RINOK(_archiveHandler->GetProperty(index, kpidLastWriteTime, &propVariant));
+    _processedFileInfo.UTCLastWriteTimeIsDefined = false;
+    switch(propVariant.vt)
+    {
+      case VT_EMPTY:
+        // _processedFileInfo.UTCLastWriteTime = _utcLastWriteTimeDefault;
+        break;
+      case VT_FILETIME:
+        _processedFileInfo.UTCLastWriteTime = propVariant.filetime;
+        _processedFileInfo.UTCLastWriteTimeIsDefined = true;
+        break;
+      default:
+        return E_FAIL;
+    }
+
+  }
+  {
+    // Get Size
+    NCOM::CPropVariant propVariant;
+    RINOK(_archiveHandler->GetProperty(index, kpidSize, &propVariant));
+    bool newFileSizeDefined = (propVariant.vt != VT_EMPTY);
+    UInt64 newFileSize;
+    if (newFileSizeDefined)
+      newFileSize = ConvertPropVariantToUInt64(propVariant);
+  }
+
+  
+  {
+    // Create folders for file
+    int slashPos = _filePath.ReverseFind(WCHAR_PATH_SEPARATOR);
+    if (slashPos >= 0)
+      NFile::NDirectory::CreateComplexDirectory(_directoryPath + _filePath.Left(slashPos));
+  }
+
+  UString fullProcessedPath = _directoryPath + _filePath;
+  _diskFilePath = fullProcessedPath;
+
+  if (_processedFileInfo.IsDirectory)
+  {
+    NFile::NDirectory::CreateComplexDirectory(fullProcessedPath);
+  }
+  else
+  {
+    NFile::NFind::CFileInfoW fileInfo;
+    if(NFile::NFind::FindFile(fullProcessedPath, fileInfo))
+    {
+      if (!NFile::NDirectory::DeleteFileAlways(fullProcessedPath))
+      {
+        PrintString(UString(kCantDeleteOutputFile) + fullProcessedPath);
+        return E_ABORT;
+      }
+    }
+    
+    _outFileStreamSpec = new COutFileStream;
+    CMyComPtr<ISequentialOutStream> outStreamLoc(_outFileStreamSpec);
+    if (!_outFileStreamSpec->Open(fullProcessedPath, CREATE_ALWAYS))
+    {
+      PrintString((UString)L"can not open output file " + fullProcessedPath);
+      return E_ABORT;
+    }
+    _outFileStream = outStreamLoc;
+    *outStream = outStreamLoc.Detach();
+  }
+  return S_OK;
+}
+
+STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode)
+{
+  _extractMode = false;
+  switch (askExtractMode)
+  {
+    case NArchive::NExtract::NAskMode::kExtract:
+      _extractMode = true;
+  };
+  switch (askExtractMode)
+  {
+    case NArchive::NExtract::NAskMode::kExtract:
+      PrintString(kExtractingString);
+      break;
+    case NArchive::NExtract::NAskMode::kTest:
+      PrintString(kTestingString);
+      break;
+    case NArchive::NExtract::NAskMode::kSkip:
+      PrintString(kSkippingString);
+      break;
+  };
+  PrintString(_filePath);
+  return S_OK;
+}
+
+STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult)
+{
+  switch(operationResult)
+  {
+    case NArchive::NExtract::NOperationResult::kOK:
+      break;
+    default:
+    {
+      NumErrors++;
+      PrintString("     ");
+      switch(operationResult)
+      {
+        case NArchive::NExtract::NOperationResult::kUnSupportedMethod:
+          PrintString(kUnsupportedMethod);
+          break;
+        case NArchive::NExtract::NOperationResult::kCRCError:
+          PrintString(kCRCFailed);
+          break;
+        case NArchive::NExtract::NOperationResult::kDataError:
+          PrintString(kDataError);
+          break;
+        default:
+          PrintString(kUnknownError);
+      }
+    }
+  }
+
+  if (_outFileStream != NULL)
+  {
+    if (_processedFileInfo.UTCLastWriteTimeIsDefined)
+      _outFileStreamSpec->SetLastWriteTime(&_processedFileInfo.UTCLastWriteTime);
+    RINOK(_outFileStreamSpec->Close());
+  }
+  _outFileStream.Release();
+  if (_extractMode && _processedFileInfo.AttributesAreDefined)
+    NFile::NDirectory::MySetFileAttributes(_diskFilePath, _processedFileInfo.Attributes);
+  PrintNewLine();
+  return S_OK;
+}
+
+
+STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password)
+{
+  if (!PasswordIsDefined)
+  {
+    // You can ask real password here from user
+    // Password = GetPassword(OutStream); 
+    // PasswordIsDefined = true;
+    PrintError("Password is not defined");
+    return E_ABORT;
+  }
+  CMyComBSTR tempName(Password);
+  *password = tempName.Detach();
+  return S_OK;
+}
+
+
+
+//////////////////////////////////////////////////////////////
+// Archive Creating callback class
+
+struct CDirItem
+{ 
+  UInt32 Attributes;
+  FILETIME CreationTime;
+  FILETIME LastAccessTime;
+  FILETIME LastWriteTime;
+  UInt64 Size;
+  UString Name;
+  UString FullPath;
+  bool IsDirectory() const { return (Attributes & FILE_ATTRIBUTE_DIRECTORY) != 0 ; }
+};
+
+class CArchiveUpdateCallback: 
+  public IArchiveUpdateCallback2,
+  public ICryptoGetTextPassword2,
+  public CMyUnknownImp
+{
+public:
+  MY_UNKNOWN_IMP2(IArchiveUpdateCallback2, ICryptoGetTextPassword2)
+
+  // IProgress
+  STDMETHOD(SetTotal)(UInt64 size);
+  STDMETHOD(SetCompleted)(const UInt64 *completeValue);
+
+  // IUpdateCallback2
+  STDMETHOD(EnumProperties)(IEnumSTATPROPSTG **enumerator);  
+  STDMETHOD(GetUpdateItemInfo)(UInt32 index, 
+      Int32 *newData, Int32 *newProperties, UInt32 *indexInArchive);
+  STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value);
+  STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **inStream);
+  STDMETHOD(SetOperationResult)(Int32 operationResult);
+  STDMETHOD(GetVolumeSize)(UInt32 index, UInt64 *size);
+  STDMETHOD(GetVolumeStream)(UInt32 index, ISequentialOutStream **volumeStream);
+
+  STDMETHOD(CryptoGetTextPassword2)(Int32 *passwordIsDefined, BSTR *password);
+
+public:
+  CRecordVector<UInt64> VolumesSizes;
+  UString VolName;
+  UString VolExt;
+
+  UString DirPrefix;
+  const CObjectVector<CDirItem> *DirItems;
+
+  bool PasswordIsDefined;
+  UString Password;
+  bool AskPassword;
+
+  bool m_NeedBeClosed;
+
+  UStringVector FailedFiles;
+  CRecordVector<HRESULT> FailedCodes;
+
+  CArchiveUpdateCallback(): PasswordIsDefined(false), AskPassword(false), DirItems(0) {};
+
+  ~CArchiveUpdateCallback() { Finilize(); }
+  HRESULT Finilize();
+
+  void Init(const CObjectVector<CDirItem> *dirItems)
+  {
+    DirItems = dirItems;
+    m_NeedBeClosed = false;
+    FailedFiles.Clear();
+    FailedCodes.Clear();
+  }
+};
+
+STDMETHODIMP CArchiveUpdateCallback::SetTotal(UInt64 /* size */)
+{
+  return S_OK;
+}
+
+STDMETHODIMP CArchiveUpdateCallback::SetCompleted(const UInt64 * /* completeValue */)
+{
+  return S_OK;
+}
+
+
+STDMETHODIMP CArchiveUpdateCallback::EnumProperties(IEnumSTATPROPSTG ** /* enumerator */)
+{
+  return E_NOTIMPL;
+}
+
+STDMETHODIMP CArchiveUpdateCallback::GetUpdateItemInfo(UInt32 /* index */, 
+      Int32 *newData, Int32 *newProperties, UInt32 *indexInArchive)
+{
+  if(newData != NULL)
+    *newData = BoolToInt(true);
+  if(newProperties != NULL)
+    *newProperties = BoolToInt(true);
+  if(indexInArchive != NULL)
+    *indexInArchive = UInt32(-1);
+  return S_OK;
+}
+
+STDMETHODIMP CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
+{
+  NWindows::NCOM::CPropVariant propVariant;
+  
+  if (propID == kpidIsAnti)
+  {
+    propVariant = false;
+    propVariant.Detach(value);
+    return S_OK;
+  }
+
+  {
+    const CDirItem &dirItem = (*DirItems)[index];
+    switch(propID)
+    {
+      case kpidPath:
+        propVariant = dirItem.Name;
+        break;
+      case kpidIsFolder:
+        propVariant = dirItem.IsDirectory();
+        break;
+      case kpidSize:
+        propVariant = dirItem.Size;
+        break;
+      case kpidAttributes:
+        propVariant = dirItem.Attributes;
+        break;
+      case kpidLastAccessTime:
+        propVariant = dirItem.LastAccessTime;
+        break;
+      case kpidCreationTime:
+        propVariant = dirItem.CreationTime;
+        break;
+      case kpidLastWriteTime:
+        propVariant = dirItem.LastWriteTime;
+        break;
+    }
+  }
+  propVariant.Detach(value);
+  return S_OK;
+}
+
+HRESULT CArchiveUpdateCallback::Finilize()
+{
+  if (m_NeedBeClosed)
+  {
+    PrintNewLine();
+    m_NeedBeClosed = false;
+  }
+  return S_OK;
+}
+
+static void GetStream2(const wchar_t *name)
+{
+  PrintString("Compressing  ");
+  if (name[0] == 0)
+    name = kEmptyFileAlias;
+  PrintString(name);
+}
+
+STDMETHODIMP CArchiveUpdateCallback::GetStream(UInt32 index, ISequentialInStream **inStream)
+{
+  RINOK(Finilize());
+
+  const CDirItem &dirItem = (*DirItems)[index];
+  GetStream2(dirItem.Name);
+ 
+  if(dirItem.IsDirectory())
+    return S_OK;
+
+  {
+    CInFileStream *inStreamSpec = new CInFileStream;
+    CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
+    UString path = DirPrefix + dirItem.FullPath;
+    if(!inStreamSpec->Open(path))
+    {
+      DWORD sysError = ::GetLastError();
+      FailedCodes.Add(sysError);
+      FailedFiles.Add(path);
+      // if (systemError == ERROR_SHARING_VIOLATION)
+      {
+        PrintNewLine();
+        PrintError("WARNING: can't open file");
+        // PrintString(NError::MyFormatMessageW(systemError));
+        return S_FALSE;
+      }
+      // return sysError;
+    }
+    *inStream = inStreamLoc.Detach();
+  }
+  return S_OK;
+}
+
+STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 /* operationResult */)
+{
+  m_NeedBeClosed = true;
+  return S_OK;
+}
+
+STDMETHODIMP CArchiveUpdateCallback::GetVolumeSize(UInt32 index, UInt64 *size)
+{
+  if (VolumesSizes.Size() == 0)
+    return S_FALSE;
+  if (index >= (UInt32)VolumesSizes.Size())
+    index = VolumesSizes.Size() - 1;
+  *size = VolumesSizes[index];
+  return S_OK;
+}
+
+STDMETHODIMP CArchiveUpdateCallback::GetVolumeStream(UInt32 index, ISequentialOutStream **volumeStream)
+{
+  wchar_t temp[32];
+  ConvertUInt64ToString(index + 1, temp);
+  UString res = temp;
+  while (res.Length() < 2)
+    res = UString(L'0') + res;
+  UString fileName = VolName;
+  fileName += L'.';
+  fileName += res;
+  fileName += VolExt;
+  COutFileStream *streamSpec = new COutFileStream;
+  CMyComPtr<ISequentialOutStream> streamLoc(streamSpec);
+  if(!streamSpec->Create(fileName, false))
+    return ::GetLastError();
+  *volumeStream = streamLoc.Detach();
+  return S_OK;
+}
+
+STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password)
+{
+  if (!PasswordIsDefined) 
+  {
+    if (AskPassword)
+    {
+      // You can ask real password here from user
+      // Password = GetPassword(OutStream); 
+      // PasswordIsDefined = true;
+      PrintError("Password is not defined");
+      return E_ABORT;
+    }
+  }
+  *passwordIsDefined = BoolToInt(PasswordIsDefined);
+  CMyComBSTR tempName(Password);
+  *password = tempName.Detach();
+  return S_OK;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+// Main function
+
+int 
+#ifdef _MSC_VER
+__cdecl 
+#endif
+main(int argc, char* argv[])
+{
+  #ifdef _WIN32
+  #ifndef _UNICODE
+  g_IsNT = IsItWindowsNT();
+  #endif
+  #endif
+
+  PrintStringLn(kCopyrightString);
+
+  if (argc < 3)
+  {
+    PrintStringLn(kHelpString);
+    return 1;
+  }
+  NWindows::NDLL::CLibrary library;
+  if (!library.Load(TEXT(kDllName)))
+  {
+    PrintError("Can not load library");
+    return 1;
+  }
+  CreateObjectFunc createObjectFunc = (CreateObjectFunc)library.GetProcAddress("CreateObject");
+  if (createObjectFunc == 0)
+  {
+    PrintError("Can not get CreateObject");
+    return 1;
+  }
+
+  AString command = argv[1];
+  UString archiveName = GetUnicodeString(argv[2], CP_OEMCP);
+  if (command.CompareNoCase("a") == 0)
+  {
+    // create archive command
+    if (argc < 4)
+    {
+      PrintStringLn(kHelpString);
+      return 1;
+    }
+    CObjectVector<CDirItem> dirItems;
+    int i;
+    for (i = 3; i < argc; i++)
+    {
+      CDirItem item;
+      UString name = GetUnicodeString(argv[i], CP_OEMCP);
+      
+      NFile::NFind::CFileInfoW fileInfo;
+      if (!NFile::NFind::FindFile(name, fileInfo))
+      {
+        PrintString(UString(L"Can't find file") + name);
+        return 1;
+      }
+
+      item.Attributes = fileInfo.Attributes;
+      item.Size = fileInfo.Size;
+      item.CreationTime = fileInfo.CreationTime;
+      item.LastAccessTime = fileInfo.LastAccessTime;
+      item.LastWriteTime = fileInfo.LastWriteTime;
+      item.Name = name;
+      item.FullPath = name;
+      dirItems.Add(item);
+    }
+    COutFileStream *outFileStreamSpec = new COutFileStream;
+    CMyComPtr<IOutStream> outFileStream = outFileStreamSpec;
+    if (!outFileStreamSpec->Create(archiveName, false))
+    {
+      PrintError("can't create archive file");
+      return 1;
+    }
+
+    CMyComPtr<IOutArchive> outArchive;
+    if (createObjectFunc(&CLSID_CFormat7z, &IID_IOutArchive, (void **)&outArchive) != S_OK)
+    {
+      PrintError("Can not get class object");
+      return 1;
+    }
+
+    CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback;
+    CMyComPtr<IArchiveUpdateCallback2> updateCallback(updateCallbackSpec);
+    updateCallbackSpec->Init(&dirItems);
+    // updateCallbackSpec->PasswordIsDefined = true;
+    // updateCallbackSpec->Password = L"1";
+
+    HRESULT result = outArchive->UpdateItems(outFileStream, dirItems.Size(), updateCallback);
+    updateCallbackSpec->Finilize();
+    if (result != S_OK)
+    {
+      PrintError("Update Error");
+      return 1;
+    }
+    for (i = 0; i < updateCallbackSpec->FailedFiles.Size(); i++)
+    {
+      PrintNewLine();
+      PrintString((UString)L"Error for file: " + updateCallbackSpec->FailedFiles[i]);
+    }
+    if (updateCallbackSpec->FailedFiles.Size() != 0)
+      return 1;
+  }
+  else
+  {
+    if (argc != 3)
+    {
+      PrintStringLn(kHelpString);
+      return 1;
+    }
+
+    bool listCommand;
+    if (command.CompareNoCase("l") == 0)
+      listCommand = true;
+    else if (command.CompareNoCase("x") == 0)
+      listCommand = false;
+    else
+    {
+      PrintError("incorrect command");
+      return 1;
+    }
+  
+    CMyComPtr<IInArchive> archive;
+    if (createObjectFunc(&CLSID_CFormat7z, &IID_IInArchive, (void **)&archive) != S_OK)
+    {
+      PrintError("Can not get class object");
+      return 1;
+    }
+    
+    CInFileStream *fileSpec = new CInFileStream;
+    CMyComPtr<IInStream> file = fileSpec;
+    
+    if (!fileSpec->Open(archiveName))
+    {
+      PrintError("Can not open archive file");
+      return 1;
+    }
+
+    {
+      CArchiveOpenCallback *openCallbackSpec = new CArchiveOpenCallback;
+      CMyComPtr<IArchiveOpenCallback> openCallback(openCallbackSpec);
+      openCallbackSpec->PasswordIsDefined = false;
+      // openCallbackSpec->PasswordIsDefined = true;
+      // openCallbackSpec->Password = L"1";
+      
+      if (archive->Open(file, 0, openCallback) != S_OK)
+      {
+        PrintError("Can not open archive");
+        return 1;
+      }
+    }
+    
+    if (listCommand)
+    {
+      // List command
+      UInt32 numItems = 0;
+      archive->GetNumberOfItems(&numItems);  
+      for (UInt32 i = 0; i < numItems; i++)
+      {
+        {
+          // Get uncompressed size of file
+          NWindows::NCOM::CPropVariant propVariant;
+          archive->GetProperty(i, kpidSize, &propVariant);
+          UString s = ConvertPropVariantToString(propVariant);
+          PrintString(s);
+          PrintString("  ");
+        }
+        {
+          // Get name of file
+          NWindows::NCOM::CPropVariant propVariant;
+          archive->GetProperty(i, kpidPath, &propVariant);
+          UString s = ConvertPropVariantToString(propVariant);
+          PrintString(s);
+        }
+        PrintString("\n");
+      }
+    }
+    else
+    {
+      // Extract command
+      CArchiveExtractCallback *extractCallbackSpec = new CArchiveExtractCallback;
+      CMyComPtr<IArchiveExtractCallback> extractCallback(extractCallbackSpec);
+      extractCallbackSpec->Init(archive, L""); // second parameter is output folder path
+      extractCallbackSpec->PasswordIsDefined = false;
+      // extractCallbackSpec->PasswordIsDefined = true;
+      // extractCallbackSpec->Password = L"1";
+      HRESULT result = archive->Extract(NULL, (UInt32)(Int32)(-1), false, extractCallback);
+      if (result != S_OK)
+      {
+        PrintError("Extract Error");
+        return 1;
+      }
+    }
+  }
+  return 0;
+}