Client7z.cpp revision baa3858d3f5d128a5c8466b700098109edcad5f2
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}
26DEFINE_GUID(CLSID_CFormat7z,
27  0x23170F69, 0x40C1, 0x278A, 0x10, 0x00, 0x00, 0x01, 0x10, 0x07, 0x00, 0x00);
28
29using namespace NWindows;
30
31#define kDllName "7z.dll"
32
33static const char *kCopyrightString = MY_7ZIP_VERSION
34" ("  kDllName " client) "
35MY_COPYRIGHT " " MY_DATE;
36
37static 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
45typedef UINT32 (WINAPI * CreateObjectFunc)(
46    const GUID *clsID,
47    const GUID *interfaceID,
48    void **outObject);
49
50
51void PrintString(const UString &s)
52{
53  printf("%s", (LPCSTR)GetOemString(s));
54}
55
56void PrintString(const AString &s)
57{
58  printf("%s", (LPCSTR)s);
59}
60
61void PrintNewLine()
62{
63  PrintString("\n");
64}
65
66void PrintStringLn(const AString &s)
67{
68  PrintString(s);
69  PrintNewLine();
70}
71
72void PrintError(const AString &s)
73{
74  PrintNewLine();
75  PrintString(s);
76  PrintNewLine();
77}
78
79static 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
92static HRESULT IsArchiveItemFolder(IInArchive *archive, UInt32 index, bool &result)
93{
94  return IsArchiveItemProp(archive, index, kpidIsDir, result);
95}
96
97
98static const wchar_t *kEmptyFileAlias = L"[Content]";
99
100
101//////////////////////////////////////////////////////////////
102// Archive Open callback class
103
104
105class CArchiveOpenCallback:
106  public IArchiveOpenCallback,
107  public ICryptoGetTextPassword,
108  public CMyUnknownImp
109{
110public:
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
124STDMETHODIMP CArchiveOpenCallback::SetTotal(const UInt64 * /* files */, const UInt64 * /* bytes */)
125{
126  return S_OK;
127}
128
129STDMETHODIMP CArchiveOpenCallback::SetCompleted(const UInt64 * /* files */, const UInt64 * /* bytes */)
130{
131  return S_OK;
132}
133
134STDMETHODIMP 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
151static const wchar_t *kCantDeleteOutputFile = L"ERROR: Can not delete output file ";
152
153static const char *kTestingString    =  "Testing     ";
154static const char *kExtractingString =  "Extracting  ";
155static const char *kSkippingString   =  "Skipping    ";
156
157static const char *kUnsupportedMethod = "Unsupported Method";
158static const char *kCRCFailed = "CRC Failed";
159static const char *kDataError = "Data Error";
160static const char *kUnknownError = "Unknown Error";
161
162class CArchiveExtractCallback:
163  public IArchiveExtractCallback,
164  public ICryptoGetTextPassword,
165  public CMyUnknownImp
166{
167public:
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
182private:
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
200public:
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
210void CArchiveExtractCallback::Init(IInArchive *archiveHandler, const UString &directoryPath)
211{
212  NumErrors = 0;
213  _archiveHandler = archiveHandler;
214  _directoryPath = directoryPath;
215  NFile::NName::NormalizeDirPathPrefix(_directoryPath);
216}
217
218STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 /* size */)
219{
220  return S_OK;
221}
222
223STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 * /* completeValue */)
224{
225  return S_OK;
226}
227
228STDMETHODIMP 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
343STDMETHODIMP 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
360STDMETHODIMP 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
401STDMETHODIMP 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
419struct 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
432class CArchiveUpdateCallback:
433  public IArchiveUpdateCallback2,
434  public ICryptoGetTextPassword2,
435  public CMyUnknownImp
436{
437public:
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
456public:
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
487STDMETHODIMP CArchiveUpdateCallback::SetTotal(UInt64 /* size */)
488{
489  return S_OK;
490}
491
492STDMETHODIMP CArchiveUpdateCallback::SetCompleted(const UInt64 * /* completeValue */)
493{
494  return S_OK;
495}
496
497
498STDMETHODIMP CArchiveUpdateCallback::EnumProperties(IEnumSTATPROPSTG ** /* enumerator */)
499{
500  return E_NOTIMPL;
501}
502
503STDMETHODIMP 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
515STDMETHODIMP 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
543HRESULT CArchiveUpdateCallback::Finilize()
544{
545  if (m_NeedBeClosed)
546  {
547    PrintNewLine();
548    m_NeedBeClosed = false;
549  }
550  return S_OK;
551}
552
553static void GetStream2(const wchar_t *name)
554{
555  PrintString("Compressing  ");
556  if (name[0] == 0)
557    name = kEmptyFileAlias;
558  PrintString(name);
559}
560
561STDMETHODIMP 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
594STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 /* operationResult */)
595{
596  m_NeedBeClosed = true;
597  return S_OK;
598}
599
600STDMETHODIMP 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
610STDMETHODIMP 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
629STDMETHODIMP 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
653int 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