• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // 7zHandlerOut.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../Common/ComTry.h"
6 #include "../../../Common/StringToInt.h"
7 #include "../../../Common/Wildcard.h"
8 
9 #include "../Common/ItemNameUtils.h"
10 #include "../Common/ParseProperties.h"
11 
12 #include "7zHandler.h"
13 #include "7zOut.h"
14 #include "7zUpdate.h"
15 
16 #ifndef EXTRACT_ONLY
17 
18 using namespace NWindows;
19 
20 namespace NArchive {
21 namespace N7z {
22 
23 #define k_LZMA_Name "LZMA"
24 #define kDefaultMethodName "LZMA2"
25 #define k_Copy_Name "Copy"
26 
27 #define k_MatchFinder_ForHeaders "BT2"
28 
29 static const UInt32 k_NumFastBytes_ForHeaders = 273;
30 static const UInt32 k_Level_ForHeaders = 5;
31 static const UInt32 k_Dictionary_ForHeaders =
32   #ifdef UNDER_CE
33   1 << 18;
34   #else
35   1 << 20;
36   #endif
37 
GetFileTimeType(UInt32 * type)38 STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type)
39 {
40   *type = NFileTimeType::kWindows;
41   return S_OK;
42 }
43 
PropsMethod_To_FullMethod(CMethodFull & dest,const COneMethodInfo & m)44 HRESULT CHandler::PropsMethod_To_FullMethod(CMethodFull &dest, const COneMethodInfo &m)
45 {
46   dest.CodecIndex = FindMethod_Index(
47       EXTERNAL_CODECS_VARS
48       m.MethodName, true,
49       dest.Id, dest.NumStreams);
50   if (dest.CodecIndex < 0)
51     return E_INVALIDARG;
52   (CProps &)dest = (CProps &)m;
53   return S_OK;
54 }
55 
SetHeaderMethod(CCompressionMethodMode & headerMethod)56 HRESULT CHandler::SetHeaderMethod(CCompressionMethodMode &headerMethod)
57 {
58   if (!_compressHeaders)
59     return S_OK;
60   COneMethodInfo m;
61   m.MethodName = k_LZMA_Name;
62   m.AddProp_Ascii(NCoderPropID::kMatchFinder, k_MatchFinder_ForHeaders);
63   m.AddProp_Level(k_Level_ForHeaders);
64   m.AddProp32(NCoderPropID::kNumFastBytes, k_NumFastBytes_ForHeaders);
65   m.AddProp32(NCoderPropID::kDictionarySize, k_Dictionary_ForHeaders);
66   m.AddProp_NumThreads(1);
67 
68   CMethodFull &methodFull = headerMethod.Methods.AddNew();
69   return PropsMethod_To_FullMethod(methodFull, m);
70 }
71 
SetMainMethod(CCompressionMethodMode & methodMode,UInt32 numThreads)72 HRESULT CHandler::SetMainMethod(
73     CCompressionMethodMode &methodMode
74     #ifndef _7ZIP_ST
75     , UInt32 numThreads
76     #endif
77     )
78 {
79   methodMode.Bonds = _bonds;
80 
81   CObjectVector<COneMethodInfo> methods = _methods;
82 
83   {
84     FOR_VECTOR (i, methods)
85     {
86       AString &methodName = methods[i].MethodName;
87       if (methodName.IsEmpty())
88         methodName = kDefaultMethodName;
89     }
90     if (methods.IsEmpty())
91     {
92       COneMethodInfo &m = methods.AddNew();
93       m.MethodName = (GetLevel() == 0 ? k_Copy_Name : kDefaultMethodName);
94       methodMode.DefaultMethod_was_Inserted = true;
95     }
96   }
97 
98   if (!_filterMethod.MethodName.IsEmpty())
99   {
100     // if (methodMode.Bonds.IsEmpty())
101     {
102       FOR_VECTOR (k, methodMode.Bonds)
103       {
104         CBond2 &bond = methodMode.Bonds[k];
105         bond.InCoder++;
106         bond.OutCoder++;
107       }
108       methods.Insert(0, _filterMethod);
109       methodMode.Filter_was_Inserted = true;
110     }
111   }
112 
113   const UInt64 kSolidBytes_Min = (1 << 24);
114   const UInt64 kSolidBytes_Max = ((UInt64)1 << 32) - 1;
115 
116   bool needSolid = false;
117 
118   FOR_VECTOR (i, methods)
119   {
120     COneMethodInfo &oneMethodInfo = methods[i];
121 
122     SetGlobalLevelTo(oneMethodInfo);
123     #ifndef _7ZIP_ST
124     CMultiMethodProps::SetMethodThreadsTo(oneMethodInfo, numThreads);
125     #endif
126 
127     CMethodFull &methodFull = methodMode.Methods.AddNew();
128     RINOK(PropsMethod_To_FullMethod(methodFull, oneMethodInfo));
129 
130     if (methodFull.Id != k_Copy)
131       needSolid = true;
132 
133     if (_numSolidBytesDefined)
134       continue;
135 
136     UInt32 dicSize;
137     switch (methodFull.Id)
138     {
139       case k_LZMA:
140       case k_LZMA2: dicSize = oneMethodInfo.Get_Lzma_DicSize(); break;
141       case k_PPMD: dicSize = oneMethodInfo.Get_Ppmd_MemSize(); break;
142       case k_Deflate: dicSize = (UInt32)1 << 15; break;
143       case k_BZip2: dicSize = oneMethodInfo.Get_BZip2_BlockSize(); break;
144       default: continue;
145     }
146 
147     _numSolidBytes = (UInt64)dicSize << 7;
148     if (_numSolidBytes < kSolidBytes_Min) _numSolidBytes = kSolidBytes_Min;
149     if (_numSolidBytes > kSolidBytes_Max) _numSolidBytes = kSolidBytes_Max;
150     _numSolidBytesDefined = true;
151   }
152 
153   if (!_numSolidBytesDefined)
154     if (needSolid)
155       _numSolidBytes = kSolidBytes_Max;
156     else
157       _numSolidBytes = 0;
158   _numSolidBytesDefined = true;
159   return S_OK;
160 }
161 
GetTime(IArchiveUpdateCallback * updateCallback,int index,PROPID propID,UInt64 & ft,bool & ftDefined)162 static HRESULT GetTime(IArchiveUpdateCallback *updateCallback, int index, PROPID propID, UInt64 &ft, bool &ftDefined)
163 {
164   // ft = 0;
165   // ftDefined = false;
166   NCOM::CPropVariant prop;
167   RINOK(updateCallback->GetProperty(index, propID, &prop));
168   if (prop.vt == VT_FILETIME)
169   {
170     ft = prop.filetime.dwLowDateTime | ((UInt64)prop.filetime.dwHighDateTime << 32);
171     ftDefined = true;
172   }
173   else if (prop.vt != VT_EMPTY)
174     return E_INVALIDARG;
175   else
176   {
177     ft = 0;
178     ftDefined = false;
179   }
180   return S_OK;
181 }
182 
183 /*
184 
185 #ifdef _WIN32
186 static const wchar_t kDirDelimiter1 = L'\\';
187 #endif
188 static const wchar_t kDirDelimiter2 = L'/';
189 
190 static inline bool IsCharDirLimiter(wchar_t c)
191 {
192   return (
193     #ifdef _WIN32
194     c == kDirDelimiter1 ||
195     #endif
196     c == kDirDelimiter2);
197 }
198 
199 static int FillSortIndex(CObjectVector<CTreeFolder> &treeFolders, int cur, int curSortIndex)
200 {
201   CTreeFolder &tf = treeFolders[cur];
202   tf.SortIndex = curSortIndex++;
203   for (int i = 0; i < tf.SubFolders.Size(); i++)
204     curSortIndex = FillSortIndex(treeFolders, tf.SubFolders[i], curSortIndex);
205   tf.SortIndexEnd = curSortIndex;
206   return curSortIndex;
207 }
208 
209 static int FindSubFolder(const CObjectVector<CTreeFolder> &treeFolders, int cur, const UString &name, int &insertPos)
210 {
211   const CIntVector &subFolders = treeFolders[cur].SubFolders;
212   int left = 0, right = subFolders.Size();
213   insertPos = -1;
214   for (;;)
215   {
216     if (left == right)
217     {
218       insertPos = left;
219       return -1;
220     }
221     int mid = (left + right) / 2;
222     int midFolder = subFolders[mid];
223     int compare = CompareFileNames(name, treeFolders[midFolder].Name);
224     if (compare == 0)
225       return midFolder;
226     if (compare < 0)
227       right = mid;
228     else
229       left = mid + 1;
230   }
231 }
232 
233 static int AddFolder(CObjectVector<CTreeFolder> &treeFolders, int cur, const UString &name)
234 {
235   int insertPos;
236   int folderIndex = FindSubFolder(treeFolders, cur, name, insertPos);
237   if (folderIndex < 0)
238   {
239     folderIndex = treeFolders.Size();
240     CTreeFolder &newFolder = treeFolders.AddNew();
241     newFolder.Parent = cur;
242     newFolder.Name = name;
243     treeFolders[cur].SubFolders.Insert(insertPos, folderIndex);
244   }
245   // else if (treeFolders[folderIndex].IsAltStreamFolder != isAltStreamFolder) throw 1123234234;
246   return folderIndex;
247 }
248 */
249 
UpdateItems(ISequentialOutStream * outStream,UInt32 numItems,IArchiveUpdateCallback * updateCallback)250 STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
251     IArchiveUpdateCallback *updateCallback)
252 {
253   COM_TRY_BEGIN
254 
255   const CDbEx *db = 0;
256   #ifdef _7Z_VOL
257   if (_volumes.Size() > 1)
258     return E_FAIL;
259   const CVolume *volume = 0;
260   if (_volumes.Size() == 1)
261   {
262     volume = &_volumes.Front();
263     db = &volume->Database;
264   }
265   #else
266   if (_inStream != 0)
267     db = &_db;
268   #endif
269 
270   if (db && !db->CanUpdate())
271     return E_NOTIMPL;
272 
273   /*
274   CMyComPtr<IArchiveGetRawProps> getRawProps;
275   updateCallback->QueryInterface(IID_IArchiveGetRawProps, (void **)&getRawProps);
276 
277   CUniqBlocks secureBlocks;
278   secureBlocks.AddUniq(NULL, 0);
279 
280   CObjectVector<CTreeFolder> treeFolders;
281   {
282     CTreeFolder folder;
283     folder.Parent = -1;
284     treeFolders.Add(folder);
285   }
286   */
287 
288   CObjectVector<CUpdateItem> updateItems;
289 
290   bool need_CTime = (Write_CTime.Def && Write_CTime.Val);
291   bool need_ATime = (Write_ATime.Def && Write_ATime.Val);
292   bool need_MTime = (Write_MTime.Def && Write_MTime.Val || !Write_MTime.Def);
293   bool need_Attrib = (Write_Attrib.Def && Write_Attrib.Val || !Write_Attrib.Def);
294 
295   if (db && !db->Files.IsEmpty())
296   {
297     if (!Write_CTime.Def) need_CTime = !db->CTime.Defs.IsEmpty();
298     if (!Write_ATime.Def) need_ATime = !db->ATime.Defs.IsEmpty();
299     if (!Write_MTime.Def) need_MTime = !db->MTime.Defs.IsEmpty();
300     if (!Write_Attrib.Def) need_Attrib = !db->Attrib.Defs.IsEmpty();
301   }
302 
303   // UString s;
304   UString name;
305 
306   for (UInt32 i = 0; i < numItems; i++)
307   {
308     Int32 newData, newProps;
309     UInt32 indexInArchive;
310     if (!updateCallback)
311       return E_FAIL;
312     RINOK(updateCallback->GetUpdateItemInfo(i, &newData, &newProps, &indexInArchive));
313     CUpdateItem ui;
314     ui.NewProps = IntToBool(newProps);
315     ui.NewData = IntToBool(newData);
316     ui.IndexInArchive = indexInArchive;
317     ui.IndexInClient = i;
318     ui.IsAnti = false;
319     ui.Size = 0;
320 
321     name.Empty();
322     // bool isAltStream = false;
323     if (ui.IndexInArchive != -1)
324     {
325       if (db == 0 || (unsigned)ui.IndexInArchive >= db->Files.Size())
326         return E_INVALIDARG;
327       const CFileItem &fi = db->Files[ui.IndexInArchive];
328       if (!ui.NewProps)
329       {
330         _db.GetPath(ui.IndexInArchive, name);
331       }
332       ui.IsDir = fi.IsDir;
333       ui.Size = fi.Size;
334       // isAltStream = fi.IsAltStream;
335       ui.IsAnti = db->IsItemAnti(ui.IndexInArchive);
336 
337       if (!ui.NewProps)
338       {
339         ui.CTimeDefined = db->CTime.GetItem(ui.IndexInArchive, ui.CTime);
340         ui.ATimeDefined = db->ATime.GetItem(ui.IndexInArchive, ui.ATime);
341         ui.MTimeDefined = db->MTime.GetItem(ui.IndexInArchive, ui.MTime);
342       }
343     }
344 
345     if (ui.NewProps)
346     {
347       bool folderStatusIsDefined;
348       if (need_Attrib)
349       {
350         NCOM::CPropVariant prop;
351         RINOK(updateCallback->GetProperty(i, kpidAttrib, &prop));
352         if (prop.vt == VT_EMPTY)
353           ui.AttribDefined = false;
354         else if (prop.vt != VT_UI4)
355           return E_INVALIDARG;
356         else
357         {
358           ui.Attrib = prop.ulVal;
359           ui.AttribDefined = true;
360         }
361       }
362 
363       // we need MTime to sort files.
364       if (need_CTime) RINOK(GetTime(updateCallback, i, kpidCTime, ui.CTime, ui.CTimeDefined));
365       if (need_ATime) RINOK(GetTime(updateCallback, i, kpidATime, ui.ATime, ui.ATimeDefined));
366       if (need_MTime) RINOK(GetTime(updateCallback, i, kpidMTime, ui.MTime, ui.MTimeDefined));
367 
368       /*
369       if (getRawProps)
370       {
371         const void *data;
372         UInt32 dataSize;
373         UInt32 propType;
374 
375         getRawProps->GetRawProp(i, kpidNtSecure, &data, &dataSize, &propType);
376         if (dataSize != 0 && propType != NPropDataType::kRaw)
377           return E_FAIL;
378         ui.SecureIndex = secureBlocks.AddUniq((const Byte *)data, dataSize);
379       }
380       */
381 
382       {
383         NCOM::CPropVariant prop;
384         RINOK(updateCallback->GetProperty(i, kpidPath, &prop));
385         if (prop.vt == VT_EMPTY)
386         {
387         }
388         else if (prop.vt != VT_BSTR)
389           return E_INVALIDARG;
390         else
391         {
392           name = prop.bstrVal;
393           NItemName::ReplaceSlashes_OsToUnix(name);
394         }
395       }
396       {
397         NCOM::CPropVariant prop;
398         RINOK(updateCallback->GetProperty(i, kpidIsDir, &prop));
399         if (prop.vt == VT_EMPTY)
400           folderStatusIsDefined = false;
401         else if (prop.vt != VT_BOOL)
402           return E_INVALIDARG;
403         else
404         {
405           ui.IsDir = (prop.boolVal != VARIANT_FALSE);
406           folderStatusIsDefined = true;
407         }
408       }
409 
410       {
411         NCOM::CPropVariant prop;
412         RINOK(updateCallback->GetProperty(i, kpidIsAnti, &prop));
413         if (prop.vt == VT_EMPTY)
414           ui.IsAnti = false;
415         else if (prop.vt != VT_BOOL)
416           return E_INVALIDARG;
417         else
418           ui.IsAnti = (prop.boolVal != VARIANT_FALSE);
419       }
420 
421       /*
422       {
423         NCOM::CPropVariant prop;
424         RINOK(updateCallback->GetProperty(i, kpidIsAltStream, &prop));
425         if (prop.vt == VT_EMPTY)
426           isAltStream = false;
427         else if (prop.vt != VT_BOOL)
428           return E_INVALIDARG;
429         else
430           isAltStream = (prop.boolVal != VARIANT_FALSE);
431       }
432       */
433 
434       if (ui.IsAnti)
435       {
436         ui.AttribDefined = false;
437 
438         ui.CTimeDefined = false;
439         ui.ATimeDefined = false;
440         ui.MTimeDefined = false;
441 
442         ui.Size = 0;
443       }
444 
445       if (!folderStatusIsDefined && ui.AttribDefined)
446         ui.SetDirStatusFromAttrib();
447     }
448     else
449     {
450       /*
451       if (_db.SecureIDs.IsEmpty())
452         ui.SecureIndex = secureBlocks.AddUniq(NULL, 0);
453       else
454       {
455         int id = _db.SecureIDs[ui.IndexInArchive];
456         size_t offs = _db.SecureOffsets[id];
457         size_t size = _db.SecureOffsets[id + 1] - offs;
458         ui.SecureIndex = secureBlocks.AddUniq(_db.SecureBuf + offs, size);
459       }
460       */
461     }
462 
463     /*
464     {
465       int folderIndex = 0;
466       if (_useParents)
467       {
468         int j;
469         s.Empty();
470         for (j = 0; j < name.Len(); j++)
471         {
472           wchar_t c = name[j];
473           if (IsCharDirLimiter(c))
474           {
475             folderIndex = AddFolder(treeFolders, folderIndex, s);
476             s.Empty();
477             continue;
478           }
479           s += c;
480         }
481         if (isAltStream)
482         {
483           int colonPos = s.Find(':');
484           if (colonPos < 0)
485           {
486             // isAltStream = false;
487             return E_INVALIDARG;
488           }
489           UString mainName = s.Left(colonPos);
490           int newFolderIndex = AddFolder(treeFolders, folderIndex, mainName);
491           if (treeFolders[newFolderIndex].UpdateItemIndex < 0)
492           {
493             for (int j = updateItems.Size() - 1; j >= 0; j--)
494             {
495               CUpdateItem &ui2 = updateItems[j];
496               if (ui2.ParentFolderIndex == folderIndex
497                   && ui2.Name == mainName)
498               {
499                 ui2.TreeFolderIndex = newFolderIndex;
500                 treeFolders[newFolderIndex].UpdateItemIndex = j;
501               }
502             }
503           }
504           folderIndex = newFolderIndex;
505           s.Delete(0, colonPos + 1);
506         }
507         ui.Name = s;
508       }
509       else
510         ui.Name = name;
511       ui.IsAltStream = isAltStream;
512       ui.ParentFolderIndex = folderIndex;
513       ui.TreeFolderIndex = -1;
514       if (ui.IsDir && !s.IsEmpty())
515       {
516         ui.TreeFolderIndex = AddFolder(treeFolders, folderIndex, s);
517         treeFolders[ui.TreeFolderIndex].UpdateItemIndex = updateItems.Size();
518       }
519     }
520     */
521     ui.Name = name;
522 
523     if (ui.NewData)
524     {
525       ui.Size = 0;
526       if (!ui.IsDir)
527       {
528         NCOM::CPropVariant prop;
529         RINOK(updateCallback->GetProperty(i, kpidSize, &prop));
530         if (prop.vt != VT_UI8)
531           return E_INVALIDARG;
532         ui.Size = (UInt64)prop.uhVal.QuadPart;
533         if (ui.Size != 0 && ui.IsAnti)
534           return E_INVALIDARG;
535       }
536     }
537 
538     updateItems.Add(ui);
539   }
540 
541   /*
542   FillSortIndex(treeFolders, 0, 0);
543   for (i = 0; i < (UInt32)updateItems.Size(); i++)
544   {
545     CUpdateItem &ui = updateItems[i];
546     ui.ParentSortIndex = treeFolders[ui.ParentFolderIndex].SortIndex;
547     ui.ParentSortIndexEnd = treeFolders[ui.ParentFolderIndex].SortIndexEnd;
548   }
549   */
550 
551   CCompressionMethodMode methodMode, headerMethod;
552 
553   HRESULT res = SetMainMethod(methodMode
554     #ifndef _7ZIP_ST
555     , _numThreads
556     #endif
557     );
558   RINOK(res);
559 
560   RINOK(SetHeaderMethod(headerMethod));
561 
562   #ifndef _7ZIP_ST
563   methodMode.NumThreads = _numThreads;
564   methodMode.MultiThreadMixer = _useMultiThreadMixer;
565   headerMethod.NumThreads = 1;
566   headerMethod.MultiThreadMixer = _useMultiThreadMixer;
567   #endif
568 
569   CMyComPtr<ICryptoGetTextPassword2> getPassword2;
570   updateCallback->QueryInterface(IID_ICryptoGetTextPassword2, (void **)&getPassword2);
571 
572   methodMode.PasswordIsDefined = false;
573   methodMode.Password.Empty();
574   if (getPassword2)
575   {
576     CMyComBSTR password;
577     Int32 passwordIsDefined;
578     RINOK(getPassword2->CryptoGetTextPassword2(&passwordIsDefined, &password));
579     methodMode.PasswordIsDefined = IntToBool(passwordIsDefined);
580     if (methodMode.PasswordIsDefined && password)
581       methodMode.Password = password;
582   }
583 
584   bool compressMainHeader = _compressHeaders;  // check it
585 
586   bool encryptHeaders = false;
587 
588   #ifndef _NO_CRYPTO
589   if (!methodMode.PasswordIsDefined && _passwordIsDefined)
590   {
591     // if header is compressed, we use that password for updated archive
592     methodMode.PasswordIsDefined = true;
593     methodMode.Password = _password;
594   }
595   #endif
596 
597   if (methodMode.PasswordIsDefined)
598   {
599     if (_encryptHeadersSpecified)
600       encryptHeaders = _encryptHeaders;
601     #ifndef _NO_CRYPTO
602     else
603       encryptHeaders = _passwordIsDefined;
604     #endif
605     compressMainHeader = true;
606     if (encryptHeaders)
607     {
608       headerMethod.PasswordIsDefined = methodMode.PasswordIsDefined;
609       headerMethod.Password = methodMode.Password;
610     }
611   }
612 
613   if (numItems < 2)
614     compressMainHeader = false;
615 
616   int level = GetLevel();
617 
618   CUpdateOptions options;
619   options.Method = &methodMode;
620   options.HeaderMethod = (_compressHeaders || encryptHeaders) ? &headerMethod : NULL;
621   options.UseFilters = (level != 0 && _autoFilter && !methodMode.Filter_was_Inserted);
622   options.MaxFilter = (level >= 8);
623   options.AnalysisLevel = GetAnalysisLevel();
624 
625   options.HeaderOptions.CompressMainHeader = compressMainHeader;
626   /*
627   options.HeaderOptions.WriteCTime = Write_CTime;
628   options.HeaderOptions.WriteATime = Write_ATime;
629   options.HeaderOptions.WriteMTime = Write_MTime;
630   options.HeaderOptions.WriteAttrib = Write_Attrib;
631   */
632 
633   options.NumSolidFiles = _numSolidFiles;
634   options.NumSolidBytes = _numSolidBytes;
635   options.SolidExtension = _solidExtension;
636   options.UseTypeSorting = _useTypeSorting;
637 
638   options.RemoveSfxBlock = _removeSfxBlock;
639   // options.VolumeMode = _volumeMode;
640 
641   options.MultiThreadMixer = _useMultiThreadMixer;
642 
643   COutArchive archive;
644   CArchiveDatabaseOut newDatabase;
645 
646   CMyComPtr<ICryptoGetTextPassword> getPassword;
647   updateCallback->QueryInterface(IID_ICryptoGetTextPassword, (void **)&getPassword);
648 
649   /*
650   if (secureBlocks.Sorted.Size() > 1)
651   {
652     secureBlocks.GetReverseMap();
653     for (int i = 0; i < updateItems.Size(); i++)
654     {
655       int &secureIndex = updateItems[i].SecureIndex;
656       secureIndex = secureBlocks.BufIndexToSortedIndex[secureIndex];
657     }
658   }
659   */
660 
661   res = Update(
662       EXTERNAL_CODECS_VARS
663       #ifdef _7Z_VOL
664       volume ? volume->Stream: 0,
665       volume ? db : 0,
666       #else
667       _inStream,
668       db,
669       #endif
670       updateItems,
671       // treeFolders,
672       // secureBlocks,
673       archive, newDatabase, outStream, updateCallback, options
674       #ifndef _NO_CRYPTO
675       , getPassword
676       #endif
677       );
678 
679   RINOK(res);
680 
681   updateItems.ClearAndFree();
682 
683   return archive.WriteDatabase(EXTERNAL_CODECS_VARS
684       newDatabase, options.HeaderMethod, options.HeaderOptions);
685 
686   COM_TRY_END
687 }
688 
ParseBond(UString & srcString,UInt32 & coder,UInt32 & stream)689 static HRESULT ParseBond(UString &srcString, UInt32 &coder, UInt32 &stream)
690 {
691   stream = 0;
692   {
693     unsigned index = ParseStringToUInt32(srcString, coder);
694     if (index == 0)
695       return E_INVALIDARG;
696     srcString.DeleteFrontal(index);
697   }
698   if (srcString[0] == 's')
699   {
700     srcString.Delete(0);
701     unsigned index = ParseStringToUInt32(srcString, stream);
702     if (index == 0)
703       return E_INVALIDARG;
704     srcString.DeleteFrontal(index);
705   }
706   return S_OK;
707 }
708 
InitProps7z()709 void COutHandler::InitProps7z()
710 {
711   _removeSfxBlock = false;
712   _compressHeaders = true;
713   _encryptHeadersSpecified = false;
714   _encryptHeaders = false;
715   // _useParents = false;
716 
717   Write_CTime.Init();
718   Write_ATime.Init();
719   Write_MTime.Init();
720   Write_Attrib.Init();
721 
722   _useMultiThreadMixer = true;
723 
724   // _volumeMode = false;
725 
726   InitSolid();
727   _useTypeSorting = false;
728 }
729 
InitProps()730 void COutHandler::InitProps()
731 {
732   CMultiMethodProps::Init();
733   InitProps7z();
734 }
735 
736 
737 
SetSolidFromString(const UString & s)738 HRESULT COutHandler::SetSolidFromString(const UString &s)
739 {
740   UString s2 = s;
741   s2.MakeLower_Ascii();
742   for (unsigned i = 0; i < s2.Len();)
743   {
744     const wchar_t *start = ((const wchar_t *)s2) + i;
745     const wchar_t *end;
746     UInt64 v = ConvertStringToUInt64(start, &end);
747     if (start == end)
748     {
749       if (s2[i++] != 'e')
750         return E_INVALIDARG;
751       _solidExtension = true;
752       continue;
753     }
754     i += (int)(end - start);
755     if (i == s2.Len())
756       return E_INVALIDARG;
757     wchar_t c = s2[i++];
758     if (c == 'f')
759     {
760       if (v < 1)
761         v = 1;
762       _numSolidFiles = v;
763     }
764     else
765     {
766       unsigned numBits;
767       switch (c)
768       {
769         case 'b': numBits =  0; break;
770         case 'k': numBits = 10; break;
771         case 'm': numBits = 20; break;
772         case 'g': numBits = 30; break;
773         case 't': numBits = 40; break;
774         default: return E_INVALIDARG;
775       }
776       _numSolidBytes = (v << numBits);
777       _numSolidBytesDefined = true;
778       /*
779       if (_numSolidBytes == 0)
780         _numSolidFiles = 1;
781       */
782     }
783   }
784   return S_OK;
785 }
786 
SetSolidFromPROPVARIANT(const PROPVARIANT & value)787 HRESULT COutHandler::SetSolidFromPROPVARIANT(const PROPVARIANT &value)
788 {
789   bool isSolid;
790   switch (value.vt)
791   {
792     case VT_EMPTY: isSolid = true; break;
793     case VT_BOOL: isSolid = (value.boolVal != VARIANT_FALSE); break;
794     case VT_BSTR:
795       if (StringToBool(value.bstrVal, isSolid))
796         break;
797       return SetSolidFromString(value.bstrVal);
798     default: return E_INVALIDARG;
799   }
800   if (isSolid)
801     InitSolid();
802   else
803     _numSolidFiles = 1;
804   return S_OK;
805 }
806 
PROPVARIANT_to_BoolPair(const PROPVARIANT & prop,CBoolPair & dest)807 static HRESULT PROPVARIANT_to_BoolPair(const PROPVARIANT &prop, CBoolPair &dest)
808 {
809   RINOK(PROPVARIANT_to_bool(prop, dest.Val));
810   dest.Def = true;
811   return S_OK;
812 }
813 
SetProperty(const wchar_t * nameSpec,const PROPVARIANT & value)814 HRESULT COutHandler::SetProperty(const wchar_t *nameSpec, const PROPVARIANT &value)
815 {
816   UString name = nameSpec;
817   name.MakeLower_Ascii();
818   if (name.IsEmpty())
819     return E_INVALIDARG;
820 
821   if (name[0] == L's')
822   {
823     name.Delete(0);
824     if (name.IsEmpty())
825       return SetSolidFromPROPVARIANT(value);
826     if (value.vt != VT_EMPTY)
827       return E_INVALIDARG;
828     return SetSolidFromString(name);
829   }
830 
831   UInt32 number;
832   int index = ParseStringToUInt32(name, number);
833   // UString realName = name.Ptr(index);
834   if (index == 0)
835   {
836     if (name.IsEqualTo("rsfx")) return PROPVARIANT_to_bool(value, _removeSfxBlock);
837     if (name.IsEqualTo("hc")) return PROPVARIANT_to_bool(value, _compressHeaders);
838     // if (name.IsEqualToNoCase(L"HS")) return PROPVARIANT_to_bool(value, _useParents);
839 
840     if (name.IsEqualTo("hcf"))
841     {
842       bool compressHeadersFull = true;
843       RINOK(PROPVARIANT_to_bool(value, compressHeadersFull));
844       return compressHeadersFull ? S_OK: E_INVALIDARG;
845     }
846 
847     if (name.IsEqualTo("he"))
848     {
849       RINOK(PROPVARIANT_to_bool(value, _encryptHeaders));
850       _encryptHeadersSpecified = true;
851       return S_OK;
852     }
853 
854     if (name.IsEqualTo("tc")) return PROPVARIANT_to_BoolPair(value, Write_CTime);
855     if (name.IsEqualTo("ta")) return PROPVARIANT_to_BoolPair(value, Write_ATime);
856     if (name.IsEqualTo("tm")) return PROPVARIANT_to_BoolPair(value, Write_MTime);
857 
858     if (name.IsEqualTo("tr")) return PROPVARIANT_to_BoolPair(value, Write_Attrib);
859 
860     if (name.IsEqualTo("mtf")) return PROPVARIANT_to_bool(value, _useMultiThreadMixer);
861 
862     if (name.IsEqualTo("qs")) return PROPVARIANT_to_bool(value, _useTypeSorting);
863 
864     // if (name.IsEqualTo("v"))  return PROPVARIANT_to_bool(value, _volumeMode);
865   }
866   return CMultiMethodProps::SetProperty(name, value);
867 }
868 
SetProperties(const wchar_t * const * names,const PROPVARIANT * values,UInt32 numProps)869 STDMETHODIMP CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps)
870 {
871   COM_TRY_BEGIN
872   _bonds.Clear();
873   InitProps();
874 
875   for (UInt32 i = 0; i < numProps; i++)
876   {
877     UString name = names[i];
878     name.MakeLower_Ascii();
879     if (name.IsEmpty())
880       return E_INVALIDARG;
881 
882     const PROPVARIANT &value = values[i];
883 
884     if (name[0] == 'b')
885     {
886       if (value.vt != VT_EMPTY)
887         return E_INVALIDARG;
888       name.Delete(0);
889 
890       CBond2 bond;
891       RINOK(ParseBond(name, bond.OutCoder, bond.OutStream));
892       if (name[0] != ':')
893         return E_INVALIDARG;
894       name.Delete(0);
895       UInt32 inStream = 0;
896       RINOK(ParseBond(name, bond.InCoder, inStream));
897       if (inStream != 0)
898         return E_INVALIDARG;
899       if (!name.IsEmpty())
900         return E_INVALIDARG;
901       _bonds.Add(bond);
902       continue;
903     }
904 
905     RINOK(SetProperty(name, value));
906   }
907 
908   unsigned numEmptyMethods = GetNumEmptyMethods();
909   if (numEmptyMethods > 0)
910   {
911     unsigned k;
912     for (k = 0; k < _bonds.Size(); k++)
913     {
914       const CBond2 &bond = _bonds[k];
915       if (bond.InCoder < (UInt32)numEmptyMethods ||
916           bond.OutCoder < (UInt32)numEmptyMethods)
917         return E_INVALIDARG;
918     }
919     for (k = 0; k < _bonds.Size(); k++)
920     {
921       CBond2 &bond = _bonds[k];
922       bond.InCoder -= (UInt32)numEmptyMethods;
923       bond.OutCoder -= (UInt32)numEmptyMethods;
924     }
925     _methods.DeleteFrontal(numEmptyMethods);
926   }
927 
928   FOR_VECTOR (k, _bonds)
929   {
930     const CBond2 &bond = _bonds[k];
931     if (bond.InCoder >= (UInt32)_methods.Size() ||
932         bond.OutCoder >= (UInt32)_methods.Size())
933       return E_INVALIDARG;
934   }
935 
936   return S_OK;
937   COM_TRY_END
938 }
939 
940 }}
941 
942 #endif
943