xref: /aosp_15_r20/external/lzma/CPP/7zip/UI/Client7z/Client7z.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // Client7z.cpp
2 
3 #include "StdAfx.h"
4 
5 #include <stdio.h>
6 
7 #include "../../../Common/MyWindows.h"
8 #include "../../../Common/MyInitGuid.h"
9 
10 #include "../../../Common/Defs.h"
11 #include "../../../Common/IntToString.h"
12 #include "../../../Common/StringConvert.h"
13 
14 #include "../../../Windows/DLL.h"
15 #include "../../../Windows/FileDir.h"
16 #include "../../../Windows/FileFind.h"
17 #include "../../../Windows/FileName.h"
18 #include "../../../Windows/NtCheck.h"
19 #include "../../../Windows/PropVariant.h"
20 #include "../../../Windows/PropVariantConv.h"
21 
22 #include "../../Common/FileStreams.h"
23 
24 #include "../../Archive/IArchive.h"
25 
26 #if 0
27 // for password request functions:
28 #include "../../UI/Console/UserInputUtils.h"
29 #endif
30 
31 #include "../../IPassword.h"
32 #include "../../../../C/7zVersion.h"
33 
34 #ifdef _WIN32
35 extern
36 HINSTANCE g_hInstance;
37 HINSTANCE g_hInstance = NULL;
38 #endif
39 
40 Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION
41 
42 // You can find full list of all GUIDs supported by 7-Zip in Guid.txt file.
43 // 7z format GUID: {23170F69-40C1-278A-1000-000110070000}
44 
45 #define DEFINE_GUID_ARC(name, id) Z7_DEFINE_GUID(name, \
46   0x23170F69, 0x40C1, 0x278A, 0x10, 0x00, 0x00, 0x01, 0x10, id, 0x00, 0x00);
47 
48 enum
49 {
50   kId_Zip = 1,
51   kId_BZip2 = 2,
52   kId_7z = 7,
53   kId_Xz = 0xC,
54   kId_Tar = 0xEE,
55   kId_GZip = 0xEF
56 };
57 
58 // use another id, if you want to support other formats (zip, Xz, ...).
59 // DEFINE_GUID_ARC (CLSID_Format, kId_Zip)
60 // DEFINE_GUID_ARC (CLSID_Format, kId_BZip2)
61 // DEFINE_GUID_ARC (CLSID_Format, kId_Xz)
62 // DEFINE_GUID_ARC (CLSID_Format, kId_Tar)
63 // DEFINE_GUID_ARC (CLSID_Format, kId_GZip)
64 DEFINE_GUID_ARC (CLSID_Format, kId_7z)
65 
66 using namespace NWindows;
67 using namespace NFile;
68 using namespace NDir;
69 
70 #ifdef _WIN32
71 #define kDllName "7z.dll"
72 #else
73 #define kDllName "7z.so"
74 #endif
75 
76 static const char * const kCopyrightString =
77   "\n"
78   "7-Zip"
79   " (" kDllName " client)"
80   " " MY_VERSION
81   " : " MY_COPYRIGHT_DATE
82   "\n";
83 
84 static const char * const kHelpString =
85 "Usage: 7zcl.exe [a | l | x] archive.7z [fileName ...]\n"
86 "Examples:\n"
87 "  7zcl.exe a archive.7z f1.txt f2.txt  : compress two files to archive.7z\n"
88 "  7zcl.exe l archive.7z   : List contents of archive.7z\n"
89 "  7zcl.exe x archive.7z   : eXtract files from archive.7z\n";
90 
91 
Convert_UString_to_AString(const UString & s,AString & temp)92 static void Convert_UString_to_AString(const UString &s, AString &temp)
93 {
94   int codePage = CP_OEMCP;
95   /*
96   int g_CodePage = -1;
97   int codePage = g_CodePage;
98   if (codePage == -1)
99     codePage = CP_OEMCP;
100   if (codePage == CP_UTF8)
101     ConvertUnicodeToUTF8(s, temp);
102   else
103   */
104     UnicodeStringToMultiByte2(temp, s, (UINT)codePage);
105 }
106 
CmdStringToFString(const char * s)107 static FString CmdStringToFString(const char *s)
108 {
109   return us2fs(GetUnicodeString(s));
110 }
111 
Print(const char * s)112 static void Print(const char *s)
113 {
114   fputs(s, stdout);
115 }
116 
Print(const AString & s)117 static void Print(const AString &s)
118 {
119   Print(s.Ptr());
120 }
121 
Print(const UString & s)122 static void Print(const UString &s)
123 {
124   AString as;
125   Convert_UString_to_AString(s, as);
126   Print(as);
127 }
128 
Print(const wchar_t * s)129 static void Print(const wchar_t *s)
130 {
131   Print(UString(s));
132 }
133 
PrintNewLine()134 static void PrintNewLine()
135 {
136   Print("\n");
137 }
138 
PrintStringLn(const char * s)139 static void PrintStringLn(const char *s)
140 {
141   Print(s);
142   PrintNewLine();
143 }
144 
PrintError(const char * message)145 static void PrintError(const char *message)
146 {
147   Print("Error: ");
148   PrintNewLine();
149   Print(message);
150   PrintNewLine();
151 }
152 
PrintError(const char * message,const FString & name)153 static void PrintError(const char *message, const FString &name)
154 {
155   PrintError(message);
156   Print(name);
157 }
158 
159 
IsArchiveItemProp(IInArchive * archive,UInt32 index,PROPID propID,bool & result)160 static HRESULT IsArchiveItemProp(IInArchive *archive, UInt32 index, PROPID propID, bool &result)
161 {
162   NCOM::CPropVariant prop;
163   RINOK(archive->GetProperty(index, propID, &prop))
164   if (prop.vt == VT_BOOL)
165     result = VARIANT_BOOLToBool(prop.boolVal);
166   else if (prop.vt == VT_EMPTY)
167     result = false;
168   else
169     return E_FAIL;
170   return S_OK;
171 }
172 
IsArchiveItemFolder(IInArchive * archive,UInt32 index,bool & result)173 static HRESULT IsArchiveItemFolder(IInArchive *archive, UInt32 index, bool &result)
174 {
175   return IsArchiveItemProp(archive, index, kpidIsDir, result);
176 }
177 
178 
179 static const wchar_t * const kEmptyFileAlias = L"[Content]";
180 
181 
182 //////////////////////////////////////////////////////////////
183 // Archive Open callback class
184 
185 
186 class CArchiveOpenCallback Z7_final:
187   public IArchiveOpenCallback,
188   public ICryptoGetTextPassword,
189   public CMyUnknownImp
190 {
191   Z7_IFACES_IMP_UNK_2(IArchiveOpenCallback, ICryptoGetTextPassword)
192 public:
193 
194   bool PasswordIsDefined;
195   UString Password;
196 
CArchiveOpenCallback()197   CArchiveOpenCallback() : PasswordIsDefined(false) {}
198 };
199 
Z7_COM7F_IMF(CArchiveOpenCallback::SetTotal (const UInt64 *,const UInt64 *))200 Z7_COM7F_IMF(CArchiveOpenCallback::SetTotal(const UInt64 * /* files */, const UInt64 * /* bytes */))
201 {
202   return S_OK;
203 }
204 
Z7_COM7F_IMF(CArchiveOpenCallback::SetCompleted (const UInt64 *,const UInt64 *))205 Z7_COM7F_IMF(CArchiveOpenCallback::SetCompleted(const UInt64 * /* files */, const UInt64 * /* bytes */))
206 {
207   return S_OK;
208 }
209 
Z7_COM7F_IMF(CArchiveOpenCallback::CryptoGetTextPassword (BSTR * password))210 Z7_COM7F_IMF(CArchiveOpenCallback::CryptoGetTextPassword(BSTR *password))
211 {
212   if (!PasswordIsDefined)
213   {
214     // You can ask real password here from user
215 #if 0
216     RINOK(GetPassword_HRESULT(&g_StdOut, Password))
217     PasswordIsDefined = true;
218 #else
219     PrintError("Password is not defined");
220     return E_ABORT;
221 #endif
222   }
223   return StringToBstr(Password, password);
224 }
225 
226 
227 
228 static const char * const kIncorrectCommand = "incorrect command";
229 
230 //////////////////////////////////////////////////////////////
231 // Archive Extracting callback class
232 
233 static const char * const kTestingString    =  "Testing     ";
234 static const char * const kExtractingString =  "Extracting  ";
235 static const char * const kSkippingString   =  "Skipping    ";
236 static const char * const kReadingString    =  "Reading     ";
237 
238 static const char * const kUnsupportedMethod = "Unsupported Method";
239 static const char * const kCRCFailed = "CRC Failed";
240 static const char * const kDataError = "Data Error";
241 static const char * const kUnavailableData = "Unavailable data";
242 static const char * const kUnexpectedEnd = "Unexpected end of data";
243 static const char * const kDataAfterEnd = "There are some data after the end of the payload data";
244 static const char * const kIsNotArc = "Is not archive";
245 static const char * const kHeadersError = "Headers Error";
246 
247 
248 struct CArcTime
249 {
250   FILETIME FT;
251   UInt16 Prec;
252   Byte Ns100;
253   bool Def;
254 
CArcTimeCArcTime255   CArcTime()
256   {
257     Clear();
258   }
259 
ClearCArcTime260   void Clear()
261   {
262     FT.dwHighDateTime = FT.dwLowDateTime = 0;
263     Prec = 0;
264     Ns100 = 0;
265     Def = false;
266   }
267 
IsZeroCArcTime268   bool IsZero() const
269   {
270     return FT.dwLowDateTime == 0 && FT.dwHighDateTime == 0 && Ns100 == 0;
271   }
272 
GetNumDigitsCArcTime273   int GetNumDigits() const
274   {
275     if (Prec == k_PropVar_TimePrec_Unix ||
276         Prec == k_PropVar_TimePrec_DOS)
277       return 0;
278     if (Prec == k_PropVar_TimePrec_HighPrec)
279       return 9;
280     if (Prec == k_PropVar_TimePrec_0)
281       return 7;
282     int digits = (int)Prec - (int)k_PropVar_TimePrec_Base;
283     if (digits < 0)
284       digits = 0;
285     return digits;
286   }
287 
Write_To_FiTimeCArcTime288   void Write_To_FiTime(CFiTime &dest) const
289   {
290    #ifdef _WIN32
291     dest = FT;
292    #else
293     if (FILETIME_To_timespec(FT, dest))
294     if ((Prec == k_PropVar_TimePrec_Base + 8 ||
295          Prec == k_PropVar_TimePrec_Base + 9)
296         && Ns100 != 0)
297     {
298       dest.tv_nsec += Ns100;
299     }
300    #endif
301   }
302 
Set_From_PropCArcTime303   void Set_From_Prop(const PROPVARIANT &prop)
304   {
305     FT = prop.filetime;
306     unsigned prec = 0;
307     unsigned ns100 = 0;
308     const unsigned prec_Temp = prop.wReserved1;
309     if (prec_Temp != 0
310         && prec_Temp <= k_PropVar_TimePrec_1ns
311         && prop.wReserved3 == 0)
312     {
313       const unsigned ns100_Temp = prop.wReserved2;
314       if (ns100_Temp < 100)
315       {
316         ns100 = ns100_Temp;
317         prec = prec_Temp;
318       }
319     }
320     Prec = (UInt16)prec;
321     Ns100 = (Byte)ns100;
322     Def = true;
323   }
324 };
325 
326 
327 
328 class CArchiveExtractCallback Z7_final:
329   public IArchiveExtractCallback,
330   public ICryptoGetTextPassword,
331   public CMyUnknownImp
332 {
333   Z7_IFACES_IMP_UNK_2(IArchiveExtractCallback, ICryptoGetTextPassword)
334   Z7_IFACE_COM7_IMP(IProgress)
335 
336   CMyComPtr<IInArchive> _archiveHandler;
337   FString _directoryPath;  // Output directory
338   UString _filePath;       // name inside arcvhive
339   FString _diskFilePath;   // full path to file on disk
340   bool _extractMode;
341   struct CProcessedFileInfo
342   {
343     CArcTime MTime;
344     UInt32 Attrib;
345     bool isDir;
346     bool Attrib_Defined;
347   } _processedFileInfo;
348 
349   COutFileStream *_outFileStreamSpec;
350   CMyComPtr<ISequentialOutStream> _outFileStream;
351 
352 public:
353   void Init(IInArchive *archiveHandler, const FString &directoryPath);
354 
355   UInt64 NumErrors;
356   bool PasswordIsDefined;
357   UString Password;
358 
CArchiveExtractCallback()359   CArchiveExtractCallback() : PasswordIsDefined(false) {}
360 };
361 
Init(IInArchive * archiveHandler,const FString & directoryPath)362 void CArchiveExtractCallback::Init(IInArchive *archiveHandler, const FString &directoryPath)
363 {
364   NumErrors = 0;
365   _archiveHandler = archiveHandler;
366   _directoryPath = directoryPath;
367   NName::NormalizeDirPathPrefix(_directoryPath);
368 }
369 
Z7_COM7F_IMF(CArchiveExtractCallback::SetTotal (UInt64))370 Z7_COM7F_IMF(CArchiveExtractCallback::SetTotal(UInt64 /* size */))
371 {
372   return S_OK;
373 }
374 
Z7_COM7F_IMF(CArchiveExtractCallback::SetCompleted (const UInt64 *))375 Z7_COM7F_IMF(CArchiveExtractCallback::SetCompleted(const UInt64 * /* completeValue */))
376 {
377   return S_OK;
378 }
379 
Z7_COM7F_IMF(CArchiveExtractCallback::GetStream (UInt32 index,ISequentialOutStream ** outStream,Int32 askExtractMode))380 Z7_COM7F_IMF(CArchiveExtractCallback::GetStream(UInt32 index,
381     ISequentialOutStream **outStream, Int32 askExtractMode))
382 {
383   *outStream = NULL;
384   _outFileStream.Release();
385 
386   {
387     // Get Name
388     NCOM::CPropVariant prop;
389     RINOK(_archiveHandler->GetProperty(index, kpidPath, &prop))
390 
391     UString fullPath;
392     if (prop.vt == VT_EMPTY)
393       fullPath = kEmptyFileAlias;
394     else
395     {
396       if (prop.vt != VT_BSTR)
397         return E_FAIL;
398       fullPath = prop.bstrVal;
399     }
400     _filePath = fullPath;
401   }
402 
403   if (askExtractMode != NArchive::NExtract::NAskMode::kExtract)
404     return S_OK;
405 
406   {
407     // Get Attrib
408     NCOM::CPropVariant prop;
409     RINOK(_archiveHandler->GetProperty(index, kpidAttrib, &prop))
410     if (prop.vt == VT_EMPTY)
411     {
412       _processedFileInfo.Attrib = 0;
413       _processedFileInfo.Attrib_Defined = false;
414     }
415     else
416     {
417       if (prop.vt != VT_UI4)
418         return E_FAIL;
419       _processedFileInfo.Attrib = prop.ulVal;
420       _processedFileInfo.Attrib_Defined = true;
421     }
422   }
423 
424   RINOK(IsArchiveItemFolder(_archiveHandler, index, _processedFileInfo.isDir))
425 
426   {
427     _processedFileInfo.MTime.Clear();
428     // Get Modified Time
429     NCOM::CPropVariant prop;
430     RINOK(_archiveHandler->GetProperty(index, kpidMTime, &prop))
431     switch (prop.vt)
432     {
433       case VT_EMPTY:
434         // _processedFileInfo.MTime = _utcMTimeDefault;
435         break;
436       case VT_FILETIME:
437         _processedFileInfo.MTime.Set_From_Prop(prop);
438         break;
439       default:
440         return E_FAIL;
441     }
442 
443   }
444   {
445     // Get Size
446     NCOM::CPropVariant prop;
447     RINOK(_archiveHandler->GetProperty(index, kpidSize, &prop))
448     UInt64 newFileSize;
449     /* bool newFileSizeDefined = */ ConvertPropVariantToUInt64(prop, newFileSize);
450   }
451 
452 
453   {
454     // Create folders for file
455     int slashPos = _filePath.ReverseFind_PathSepar();
456     if (slashPos >= 0)
457       CreateComplexDir(_directoryPath + us2fs(_filePath.Left(slashPos)));
458   }
459 
460   FString fullProcessedPath = _directoryPath + us2fs(_filePath);
461   _diskFilePath = fullProcessedPath;
462 
463   if (_processedFileInfo.isDir)
464   {
465     CreateComplexDir(fullProcessedPath);
466   }
467   else
468   {
469     NFind::CFileInfo fi;
470     if (fi.Find(fullProcessedPath))
471     {
472       if (!DeleteFileAlways(fullProcessedPath))
473       {
474         PrintError("Cannot delete output file", fullProcessedPath);
475         return E_ABORT;
476       }
477     }
478 
479     _outFileStreamSpec = new COutFileStream;
480     CMyComPtr<ISequentialOutStream> outStreamLoc(_outFileStreamSpec);
481     if (!_outFileStreamSpec->Create_ALWAYS(fullProcessedPath))
482     {
483       PrintError("Cannot open output file", fullProcessedPath);
484       return E_ABORT;
485     }
486     _outFileStream = outStreamLoc;
487     *outStream = outStreamLoc.Detach();
488   }
489   return S_OK;
490 }
491 
Z7_COM7F_IMF(CArchiveExtractCallback::PrepareOperation (Int32 askExtractMode))492 Z7_COM7F_IMF(CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode))
493 {
494   _extractMode = false;
495   switch (askExtractMode)
496   {
497     case NArchive::NExtract::NAskMode::kExtract:  _extractMode = true; break;
498   }
499   switch (askExtractMode)
500   {
501     case NArchive::NExtract::NAskMode::kExtract:  Print(kExtractingString); break;
502     case NArchive::NExtract::NAskMode::kTest:  Print(kTestingString); break;
503     case NArchive::NExtract::NAskMode::kSkip:  Print(kSkippingString); break;
504     case NArchive::NExtract::NAskMode::kReadExternal: Print(kReadingString); break;
505     default:
506       Print("??? "); break;
507   }
508   Print(_filePath);
509   return S_OK;
510 }
511 
Z7_COM7F_IMF(CArchiveExtractCallback::SetOperationResult (Int32 operationResult))512 Z7_COM7F_IMF(CArchiveExtractCallback::SetOperationResult(Int32 operationResult))
513 {
514   switch (operationResult)
515   {
516     case NArchive::NExtract::NOperationResult::kOK:
517       break;
518     default:
519     {
520       NumErrors++;
521       Print("  :  ");
522       const char *s = NULL;
523       switch (operationResult)
524       {
525         case NArchive::NExtract::NOperationResult::kUnsupportedMethod:
526           s = kUnsupportedMethod;
527           break;
528         case NArchive::NExtract::NOperationResult::kCRCError:
529           s = kCRCFailed;
530           break;
531         case NArchive::NExtract::NOperationResult::kDataError:
532           s = kDataError;
533           break;
534         case NArchive::NExtract::NOperationResult::kUnavailable:
535           s = kUnavailableData;
536           break;
537         case NArchive::NExtract::NOperationResult::kUnexpectedEnd:
538           s = kUnexpectedEnd;
539           break;
540         case NArchive::NExtract::NOperationResult::kDataAfterEnd:
541           s = kDataAfterEnd;
542           break;
543         case NArchive::NExtract::NOperationResult::kIsNotArc:
544           s = kIsNotArc;
545           break;
546         case NArchive::NExtract::NOperationResult::kHeadersError:
547           s = kHeadersError;
548           break;
549       }
550       if (s)
551       {
552         Print("Error : ");
553         Print(s);
554       }
555       else
556       {
557         char temp[16];
558         ConvertUInt32ToString((UInt32)operationResult, temp);
559         Print("Error #");
560         Print(temp);
561       }
562     }
563   }
564 
565   if (_outFileStream)
566   {
567     if (_processedFileInfo.MTime.Def)
568     {
569       CFiTime ft;
570       _processedFileInfo.MTime.Write_To_FiTime(ft);
571       _outFileStreamSpec->SetMTime(&ft);
572     }
573     RINOK(_outFileStreamSpec->Close())
574   }
575   _outFileStream.Release();
576   if (_extractMode && _processedFileInfo.Attrib_Defined)
577     SetFileAttrib_PosixHighDetect(_diskFilePath, _processedFileInfo.Attrib);
578   PrintNewLine();
579   return S_OK;
580 }
581 
582 
Z7_COM7F_IMF(CArchiveExtractCallback::CryptoGetTextPassword (BSTR * password))583 Z7_COM7F_IMF(CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password))
584 {
585   if (!PasswordIsDefined)
586   {
587 #if 0
588     // You can ask real password here from user
589     RINOK(GetPassword_HRESULT(&g_StdOut, Password))
590     PasswordIsDefined = true;
591 #else
592     PrintError("Password is not defined");
593     return E_ABORT;
594 #endif
595   }
596   return StringToBstr(Password, password);
597 }
598 
599 
600 
601 //////////////////////////////////////////////////////////////
602 // Archive Creating callback class
603 
604 struct CDirItem: public NWindows::NFile::NFind::CFileInfoBase
605 {
606   UString Path_For_Handler;
607   FString FullPath; // for filesystem
608 
CDirItemCDirItem609   CDirItem(const NWindows::NFile::NFind::CFileInfo &fi):
610       CFileInfoBase(fi)
611     {}
612 };
613 
614 class CArchiveUpdateCallback Z7_final:
615   public IArchiveUpdateCallback2,
616   public ICryptoGetTextPassword2,
617   public CMyUnknownImp
618 {
619   Z7_IFACES_IMP_UNK_2(IArchiveUpdateCallback2, ICryptoGetTextPassword2)
620   Z7_IFACE_COM7_IMP(IProgress)
621   Z7_IFACE_COM7_IMP(IArchiveUpdateCallback)
622 
623 public:
624   CRecordVector<UInt64> VolumesSizes;
625   UString VolName;
626   UString VolExt;
627 
628   FString DirPrefix;
629   const CObjectVector<CDirItem> *DirItems;
630 
631   bool PasswordIsDefined;
632   UString Password;
633   bool AskPassword;
634 
635   bool m_NeedBeClosed;
636 
637   FStringVector FailedFiles;
638   CRecordVector<HRESULT> FailedCodes;
639 
CArchiveUpdateCallback()640   CArchiveUpdateCallback():
641       DirItems(NULL),
642       PasswordIsDefined(false),
643       AskPassword(false)
644       {}
645 
~CArchiveUpdateCallback()646   ~CArchiveUpdateCallback() { Finilize(); }
647   HRESULT Finilize();
648 
Init(const CObjectVector<CDirItem> * dirItems)649   void Init(const CObjectVector<CDirItem> *dirItems)
650   {
651     DirItems = dirItems;
652     m_NeedBeClosed = false;
653     FailedFiles.Clear();
654     FailedCodes.Clear();
655   }
656 };
657 
Z7_COM7F_IMF(CArchiveUpdateCallback::SetTotal (UInt64))658 Z7_COM7F_IMF(CArchiveUpdateCallback::SetTotal(UInt64 /* size */))
659 {
660   return S_OK;
661 }
662 
Z7_COM7F_IMF(CArchiveUpdateCallback::SetCompleted (const UInt64 *))663 Z7_COM7F_IMF(CArchiveUpdateCallback::SetCompleted(const UInt64 * /* completeValue */))
664 {
665   return S_OK;
666 }
667 
Z7_COM7F_IMF(CArchiveUpdateCallback::GetUpdateItemInfo (UInt32,Int32 * newData,Int32 * newProperties,UInt32 * indexInArchive))668 Z7_COM7F_IMF(CArchiveUpdateCallback::GetUpdateItemInfo(UInt32 /* index */,
669       Int32 *newData, Int32 *newProperties, UInt32 *indexInArchive))
670 {
671   if (newData)
672     *newData = BoolToInt(true);
673   if (newProperties)
674     *newProperties = BoolToInt(true);
675   if (indexInArchive)
676     *indexInArchive = (UInt32)(Int32)-1;
677   return S_OK;
678 }
679 
Z7_COM7F_IMF(CArchiveUpdateCallback::GetProperty (UInt32 index,PROPID propID,PROPVARIANT * value))680 Z7_COM7F_IMF(CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
681 {
682   NCOM::CPropVariant prop;
683 
684   if (propID == kpidIsAnti)
685   {
686     prop = false;
687     prop.Detach(value);
688     return S_OK;
689   }
690 
691   {
692     const CDirItem &di = (*DirItems)[index];
693     switch (propID)
694     {
695       case kpidPath:  prop = di.Path_For_Handler; break;
696       case kpidIsDir:  prop = di.IsDir(); break;
697       case kpidSize:  prop = di.Size; break;
698       case kpidCTime:  PropVariant_SetFrom_FiTime(prop, di.CTime); break;
699       case kpidATime:  PropVariant_SetFrom_FiTime(prop, di.ATime); break;
700       case kpidMTime:  PropVariant_SetFrom_FiTime(prop, di.MTime); break;
701       case kpidAttrib:  prop = (UInt32)di.GetWinAttrib(); break;
702       case kpidPosixAttrib: prop = (UInt32)di.GetPosixAttrib(); break;
703     }
704   }
705   prop.Detach(value);
706   return S_OK;
707 }
708 
Finilize()709 HRESULT CArchiveUpdateCallback::Finilize()
710 {
711   if (m_NeedBeClosed)
712   {
713     PrintNewLine();
714     m_NeedBeClosed = false;
715   }
716   return S_OK;
717 }
718 
GetStream2(const wchar_t * name)719 static void GetStream2(const wchar_t *name)
720 {
721   Print("Compressing  ");
722   if (name[0] == 0)
723     name = kEmptyFileAlias;
724   Print(name);
725 }
726 
Z7_COM7F_IMF(CArchiveUpdateCallback::GetStream (UInt32 index,ISequentialInStream ** inStream))727 Z7_COM7F_IMF(CArchiveUpdateCallback::GetStream(UInt32 index, ISequentialInStream **inStream))
728 {
729   RINOK(Finilize())
730 
731   const CDirItem &dirItem = (*DirItems)[index];
732   GetStream2(dirItem.Path_For_Handler);
733 
734   if (dirItem.IsDir())
735     return S_OK;
736 
737   {
738     CInFileStream *inStreamSpec = new CInFileStream;
739     CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
740     FString path = DirPrefix + dirItem.FullPath;
741     if (!inStreamSpec->Open(path))
742     {
743       const DWORD sysError = ::GetLastError();
744       FailedCodes.Add(HRESULT_FROM_WIN32(sysError));
745       FailedFiles.Add(path);
746       // if (systemError == ERROR_SHARING_VIOLATION)
747       {
748         PrintNewLine();
749         PrintError("WARNING: can't open file");
750         // Print(NError::MyFormatMessageW(systemError));
751         return S_FALSE;
752       }
753       // return sysError;
754     }
755     *inStream = inStreamLoc.Detach();
756   }
757   return S_OK;
758 }
759 
Z7_COM7F_IMF(CArchiveUpdateCallback::SetOperationResult (Int32))760 Z7_COM7F_IMF(CArchiveUpdateCallback::SetOperationResult(Int32 /* operationResult */))
761 {
762   m_NeedBeClosed = true;
763   return S_OK;
764 }
765 
Z7_COM7F_IMF(CArchiveUpdateCallback::GetVolumeSize (UInt32 index,UInt64 * size))766 Z7_COM7F_IMF(CArchiveUpdateCallback::GetVolumeSize(UInt32 index, UInt64 *size))
767 {
768   if (VolumesSizes.Size() == 0)
769     return S_FALSE;
770   if (index >= (UInt32)VolumesSizes.Size())
771     index = VolumesSizes.Size() - 1;
772   *size = VolumesSizes[index];
773   return S_OK;
774 }
775 
Z7_COM7F_IMF(CArchiveUpdateCallback::GetVolumeStream (UInt32 index,ISequentialOutStream ** volumeStream))776 Z7_COM7F_IMF(CArchiveUpdateCallback::GetVolumeStream(UInt32 index, ISequentialOutStream **volumeStream))
777 {
778   wchar_t temp[16];
779   ConvertUInt32ToString(index + 1, temp);
780   UString res = temp;
781   while (res.Len() < 2)
782     res.InsertAtFront(L'0');
783   UString fileName = VolName;
784   fileName.Add_Dot();
785   fileName += res;
786   fileName += VolExt;
787   COutFileStream *streamSpec = new COutFileStream;
788   CMyComPtr<ISequentialOutStream> streamLoc(streamSpec);
789   if (!streamSpec->Create_NEW(us2fs(fileName)))
790     return GetLastError_noZero_HRESULT();
791   *volumeStream = streamLoc.Detach();
792   return S_OK;
793 }
794 
Z7_COM7F_IMF(CArchiveUpdateCallback::CryptoGetTextPassword2 (Int32 * passwordIsDefined,BSTR * password))795 Z7_COM7F_IMF(CArchiveUpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password))
796 {
797   if (!PasswordIsDefined)
798   {
799     if (AskPassword)
800     {
801 #if 0
802       RINOK(GetPassword_HRESULT(&g_StdOut, Password))
803       PasswordIsDefined = true;
804 #else
805       PrintError("Password is not defined");
806       return E_ABORT;
807 #endif
808     }
809   }
810   *passwordIsDefined = BoolToInt(PasswordIsDefined);
811   return StringToBstr(Password, password);
812 }
813 
814 
815 // Main function
816 
817 #if defined(_UNICODE) && !defined(_WIN64) && !defined(UNDER_CE)
818 #define NT_CHECK_FAIL_ACTION PrintError("Unsupported Windows version"); return 1;
819 #endif
820 
main(int numArgs,const char * args[])821 int Z7_CDECL main(int numArgs, const char *args[])
822 {
823   NT_CHECK
824 
825   #ifdef ENV_HAVE_LOCALE
826   MY_SetLocale();
827   #endif
828 
829   PrintStringLn(kCopyrightString);
830 
831   if (numArgs < 2)
832   {
833     PrintStringLn(kHelpString);
834     return 0;
835   }
836 
837   FString dllPrefix;
838 
839   #ifdef _WIN32
840   dllPrefix = NDLL::GetModuleDirPrefix();
841   #else
842   {
843     AString s (args[0]);
844     int sep = s.ReverseFind_PathSepar();
845     s.DeleteFrom(sep + 1);
846     dllPrefix = s;
847   }
848   #endif
849 
850   NDLL::CLibrary lib;
851   if (!lib.Load(dllPrefix + FTEXT(kDllName)))
852   {
853     PrintError("Cannot load 7-zip library");
854     return 1;
855   }
856 
857 #if defined(__clang__)
858 #pragma GCC diagnostic ignored "-Wc++98-compat-pedantic"
859 #endif
860 
861 #ifdef _WIN32
862 Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION
863 #endif
864 
865   Func_CreateObject
866      f_CreateObject = Z7_GET_PROC_ADDRESS(
867   Func_CreateObject, lib.Get_HMODULE(),
868       "CreateObject");
869   if (!f_CreateObject)
870   {
871     PrintError("Cannot get CreateObject");
872     return 1;
873   }
874 
875   char c = 0;
876   UString password;
877   bool passwordIsDefined = false;
878   CObjectVector<FString> params;
879 
880   for (int curCmd = 1; curCmd < numArgs; curCmd++)
881   {
882     AString a(args[curCmd]);
883 
884     if (!a.IsEmpty())
885     {
886       if (a[0] == '-')
887       {
888         if (!passwordIsDefined && a[1] == 'p')
889         {
890           password = GetUnicodeString(a.Ptr(2));
891           passwordIsDefined = true;
892           continue;
893         }
894       }
895       else
896       {
897         if (c)
898         {
899           params.Add(CmdStringToFString(a));
900           continue;
901         }
902         if (a.Len() == 1)
903         {
904           c = (char)MyCharLower_Ascii(a[0]);
905           continue;
906         }
907       }
908     }
909     {
910       PrintError(kIncorrectCommand);
911       return 1;
912     }
913   }
914 
915   if (!c || params.Size() < 1)
916   {
917     PrintError(kIncorrectCommand);
918     return 1;
919   }
920 
921   const FString &archiveName = params[0];
922 
923   if (c == 'a')
924   {
925     // create archive command
926     if (params.Size() < 2)
927     {
928       PrintError(kIncorrectCommand);
929       return 1;
930     }
931     CObjectVector<CDirItem> dirItems;
932     {
933       unsigned i;
934       for (i = 1; i < params.Size(); i++)
935       {
936         const FString &name = params[i];
937 
938         NFind::CFileInfo fi;
939         if (!fi.Find(name))
940         {
941           PrintError("Can't find file", name);
942           return 1;
943         }
944 
945         CDirItem di(fi);
946 
947         di.Path_For_Handler = fs2us(name);
948         di.FullPath = name;
949         dirItems.Add(di);
950       }
951     }
952 
953     COutFileStream *outFileStreamSpec = new COutFileStream;
954     CMyComPtr<IOutStream> outFileStream = outFileStreamSpec;
955     if (!outFileStreamSpec->Create_NEW(archiveName))
956     {
957       PrintError("can't create archive file");
958       return 1;
959     }
960 
961     CMyComPtr<IOutArchive> outArchive;
962     if (f_CreateObject(&CLSID_Format, &IID_IOutArchive, (void **)&outArchive) != S_OK)
963     {
964       PrintError("Cannot get class object");
965       return 1;
966     }
967 
968     CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback;
969     CMyComPtr<IArchiveUpdateCallback2> updateCallback(updateCallbackSpec);
970     updateCallbackSpec->Init(&dirItems);
971     updateCallbackSpec->PasswordIsDefined = passwordIsDefined;
972     updateCallbackSpec->Password = password;
973 
974     /*
975     {
976       const wchar_t *names[] =
977       {
978         L"m",
979         L"s",
980         L"x"
981       };
982       const unsigned kNumProps = Z7_ARRAY_SIZE(names);
983       NCOM::CPropVariant values[kNumProps] =
984       {
985         L"lzma",
986         false,    // solid mode OFF
987         (UInt32)9 // compression level = 9 - ultra
988       };
989       CMyComPtr<ISetProperties> setProperties;
990       outArchive->QueryInterface(IID_ISetProperties, (void **)&setProperties);
991       if (!setProperties)
992       {
993         PrintError("ISetProperties unsupported");
994         return 1;
995       }
996       if (setProperties->SetProperties(names, values, kNumProps) != S_OK)
997       {
998         PrintError("SetProperties() error");
999         return 1;
1000       }
1001     }
1002     */
1003 
1004     HRESULT result = outArchive->UpdateItems(outFileStream, dirItems.Size(), updateCallback);
1005 
1006     updateCallbackSpec->Finilize();
1007 
1008     if (result != S_OK)
1009     {
1010       PrintError("Update Error");
1011       return 1;
1012     }
1013 
1014     FOR_VECTOR (i, updateCallbackSpec->FailedFiles)
1015     {
1016       PrintNewLine();
1017       PrintError("Error for file", updateCallbackSpec->FailedFiles[i]);
1018     }
1019 
1020     if (updateCallbackSpec->FailedFiles.Size() != 0)
1021       return 1;
1022   }
1023   else
1024   {
1025     if (params.Size() != 1)
1026     {
1027       PrintError(kIncorrectCommand);
1028       return 1;
1029     }
1030 
1031     bool listCommand;
1032 
1033     if (c == 'l')
1034       listCommand = true;
1035     else if (c == 'x')
1036       listCommand = false;
1037     else
1038     {
1039       PrintError(kIncorrectCommand);
1040       return 1;
1041     }
1042 
1043     CMyComPtr<IInArchive> archive;
1044     if (f_CreateObject(&CLSID_Format, &IID_IInArchive, (void **)&archive) != S_OK)
1045     {
1046       PrintError("Cannot get class object");
1047       return 1;
1048     }
1049 
1050     CInFileStream *fileSpec = new CInFileStream;
1051     CMyComPtr<IInStream> file = fileSpec;
1052 
1053     if (!fileSpec->Open(archiveName))
1054     {
1055       PrintError("Cannot open archive file", archiveName);
1056       return 1;
1057     }
1058 
1059     {
1060       CArchiveOpenCallback *openCallbackSpec = new CArchiveOpenCallback;
1061       CMyComPtr<IArchiveOpenCallback> openCallback(openCallbackSpec);
1062       openCallbackSpec->PasswordIsDefined = passwordIsDefined;
1063       openCallbackSpec->Password = password;
1064 
1065       const UInt64 scanSize = 1 << 23;
1066       if (archive->Open(file, &scanSize, openCallback) != S_OK)
1067       {
1068         PrintError("Cannot open file as archive", archiveName);
1069         return 1;
1070       }
1071     }
1072 
1073     if (listCommand)
1074     {
1075       // List command
1076       UInt32 numItems = 0;
1077       archive->GetNumberOfItems(&numItems);
1078       for (UInt32 i = 0; i < numItems; i++)
1079       {
1080         {
1081           // Get uncompressed size of file
1082           NCOM::CPropVariant prop;
1083           archive->GetProperty(i, kpidSize, &prop);
1084           char s[64];
1085           ConvertPropVariantToShortString(prop, s);
1086           Print(s);
1087           Print("  ");
1088         }
1089         {
1090           // Get name of file
1091           NCOM::CPropVariant prop;
1092           archive->GetProperty(i, kpidPath, &prop);
1093           if (prop.vt == VT_BSTR)
1094             Print(prop.bstrVal);
1095           else if (prop.vt != VT_EMPTY)
1096             Print("ERROR!");
1097         }
1098         PrintNewLine();
1099       }
1100     }
1101     else
1102     {
1103       // Extract command
1104       CArchiveExtractCallback *extractCallbackSpec = new CArchiveExtractCallback;
1105       CMyComPtr<IArchiveExtractCallback> extractCallback(extractCallbackSpec);
1106       extractCallbackSpec->Init(archive, FString()); // second parameter is output folder path
1107       extractCallbackSpec->PasswordIsDefined = passwordIsDefined;
1108       extractCallbackSpec->Password = password;
1109 
1110       /*
1111       const wchar_t *names[] =
1112       {
1113         L"mt",
1114         L"mtf"
1115       };
1116       const unsigned kNumProps = sizeof(names) / sizeof(names[0]);
1117       NCOM::CPropVariant values[kNumProps] =
1118       {
1119         (UInt32)1,
1120         false
1121       };
1122       CMyComPtr<ISetProperties> setProperties;
1123       archive->QueryInterface(IID_ISetProperties, (void **)&setProperties);
1124       if (setProperties)
1125       {
1126         if (setProperties->SetProperties(names, values, kNumProps) != S_OK)
1127         {
1128           PrintError("SetProperties() error");
1129           return 1;
1130         }
1131       }
1132       */
1133 
1134       HRESULT result = archive->Extract(NULL, (UInt32)(Int32)(-1), false, extractCallback);
1135 
1136       if (result != S_OK)
1137       {
1138         PrintError("Extract Error");
1139         return 1;
1140       }
1141     }
1142   }
1143 
1144   return 0;
1145 }
1146