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