Home | History | Annotate | Download | only in Client7z
      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 
     51 void PrintString(const UString &s)
     52 {
     53   printf("%s", (LPCSTR)GetOemString(s));
     54 }
     55 
     56 void PrintString(const AString &s)
     57 {
     58   printf("%s", (LPCSTR)s);
     59 }
     60 
     61 void PrintNewLine()
     62 {
     63   PrintString("\n");
     64 }
     65 
     66 void PrintStringLn(const AString &s)
     67 {
     68   PrintString(s);
     69   PrintNewLine();
     70 }
     71 
     72 void PrintError(const AString &s)
     73 {
     74   PrintNewLine();
     75   PrintString(s);
     76   PrintNewLine();
     77 }
     78 
     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 
     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 
    121   CArchiveOpenCallback() : PasswordIsDefined(false) {}
    122 };
    123 
    124 STDMETHODIMP CArchiveOpenCallback::SetTotal(const UInt64 * /* files */, const UInt64 * /* bytes */)
    125 {
    126   return S_OK;
    127 }
    128 
    129 STDMETHODIMP CArchiveOpenCallback::SetCompleted(const UInt64 * /* files */, const UInt64 * /* bytes */)
    130 {
    131   return S_OK;
    132 }
    133 
    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 
    207   CArchiveExtractCallback() : PasswordIsDefined(false) {}
    208 };
    209 
    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 
    218 STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 /* size */)
    219 {
    220   return S_OK;
    221 }
    222 
    223 STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 * /* completeValue */)
    224 {
    225   return S_OK;
    226 }
    227 
    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 
    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 
    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 
    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 
    429   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 
    473   CArchiveUpdateCallback(): PasswordIsDefined(false), AskPassword(false), DirItems(0) {};
    474 
    475   ~CArchiveUpdateCallback() { Finilize(); }
    476   HRESULT Finilize();
    477 
    478   void Init(const CObjectVector<CDirItem> *dirItems)
    479   {
    480     DirItems = dirItems;
    481     m_NeedBeClosed = false;
    482     FailedFiles.Clear();
    483     FailedCodes.Clear();
    484   }
    485 };
    486 
    487 STDMETHODIMP CArchiveUpdateCallback::SetTotal(UInt64 /* size */)
    488 {
    489   return S_OK;
    490 }
    491 
    492 STDMETHODIMP CArchiveUpdateCallback::SetCompleted(const UInt64 * /* completeValue */)
    493 {
    494   return S_OK;
    495 }
    496 
    497 
    498 STDMETHODIMP CArchiveUpdateCallback::EnumProperties(IEnumSTATPROPSTG ** /* enumerator */)
    499 {
    500   return E_NOTIMPL;
    501 }
    502 
    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 
    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 
    543 HRESULT CArchiveUpdateCallback::Finilize()
    544 {
    545   if (m_NeedBeClosed)
    546   {
    547     PrintNewLine();
    548     m_NeedBeClosed = false;
    549   }
    550   return S_OK;
    551 }
    552 
    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 
    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 
    594 STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 /* operationResult */)
    595 {
    596   m_NeedBeClosed = true;
    597   return S_OK;
    598 }
    599 
    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 
    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 
    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 
    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