xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/ArHandler.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // ArHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../C/CpuArch.h"
6 
7 #include "../../Common/ComTry.h"
8 #include "../../Common/IntToString.h"
9 #include "../../Common/StringConvert.h"
10 #include "../../Common/StringToInt.h"
11 
12 #include "../../Windows/PropVariant.h"
13 #include "../../Windows/TimeUtils.h"
14 
15 #include "../Common/LimitedStreams.h"
16 #include "../Common/ProgressUtils.h"
17 #include "../Common/RegisterArc.h"
18 #include "../Common/StreamObjects.h"
19 #include "../Common/StreamUtils.h"
20 
21 #include "../Compress/CopyCoder.h"
22 
23 #include "Common/ItemNameUtils.h"
24 
25 using namespace NWindows;
26 using namespace NTime;
27 
28 namespace NArchive {
29 namespace NAr {
30 
31 /*
32 The end of each file member (including last file in archive) is 2-bytes aligned.
33 It uses 0xA padding if required.
34 
35 File Names:
36 
37 GNU/SVR4 variant (.a static library):
38   /       - archive symbol table
39   //      - the list of the long filenames, separated by one or more LF characters.
40   /N      - the reference to name string in long filenames list
41   name/   - the name
42 
43 Microsoft variant (.lib static library):
44   /       - First linker file (archive symbol table)
45   /       - Second linker file
46   //      - the list of the long filenames, null-terminated. Each string begins
47             immediately after the null byte in the previous string.
48   /N      - the reference to name string in long filenames list
49   name/   - the name
50 
51 BSD (Mac OS X) variant:
52   "__.SYMDEF"         -  archive symbol table
53     or
54   "__.SYMDEF SORTED"  -  archive symbol table
55   #1/N    - the real filename of length N is appended to the file header.
56 */
57 
58 static const unsigned kSignatureLen = 8;
59 static const Byte kSignature[kSignatureLen] =
60   { '!', '<', 'a', 'r', 'c', 'h', '>', 0x0A };
61 
62 static const unsigned kNameSize = 16;
63 static const unsigned kTimeSize = 12;
64 static const unsigned kUserSize = 6;
65 static const unsigned kModeSize = 8;
66 static const unsigned kSizeSize = 10;
67 
68 static const unsigned kHeaderSize = kNameSize + kTimeSize + kUserSize * 2 + kModeSize + kSizeSize + 1 + 1;
69 
70 enum EType
71 {
72   kType_Ar,
73   kType_ALib,
74   kType_Deb,
75   kType_Lib
76 };
77 
78 static const char * const k_TypeExtionsions[] =
79 {
80     "ar"
81   , "a"
82   , "deb"
83   , "lib"
84 };
85 
86 enum ESubType
87 {
88   kSubType_None,
89   kSubType_BSD
90 };
91 
92 /*
93 struct CHeader
94 {
95   char Name[kNameSize];
96   char MTime[kTimeSize];
97   char User[kUserSize];
98   char Group[kUserSize];
99   char Mode[kModeSize];
100   char Size[kSizeSize];
101   char Quote;
102   char NewLine;
103 };
104 */
105 
106 struct CItem
107 {
108   AString Name;
109   UInt64 Size;
110   UInt32 MTime;
111   UInt32 User;
112   UInt32 Group;
113   UInt32 Mode;
114 
115   UInt64 HeaderPos;
116   UInt64 HeaderSize;
117 
118   int TextFileIndex;
119   int SameNameIndex;
120 
CItemNArchive::NAr::CItem121   CItem(): TextFileIndex(-1), SameNameIndex(-1) {}
GetDataPosNArchive::NAr::CItem122   UInt64 GetDataPos() const { return HeaderPos + HeaderSize; }
123 };
124 
125 class CInArchive
126 {
127   CMyComPtr<IInStream> m_Stream;
128 
129 public:
130   UInt64 Position;
131   ESubType SubType;
132 
133   HRESULT GetNextItem(CItem &itemInfo, bool &filled);
134   HRESULT Open(IInStream *inStream);
SkipData(UInt64 dataSize)135   HRESULT SkipData(UInt64 dataSize)
136   {
137     return m_Stream->Seek((Int64)(dataSize + (dataSize & 1)), STREAM_SEEK_CUR, &Position);
138   }
139 };
140 
Open(IInStream * inStream)141 HRESULT CInArchive::Open(IInStream *inStream)
142 {
143   SubType = kSubType_None;
144   RINOK(InStream_GetPos(inStream, Position))
145   char signature[kSignatureLen];
146   RINOK(ReadStream_FALSE(inStream, signature, kSignatureLen))
147   Position += kSignatureLen;
148   if (memcmp(signature, kSignature, kSignatureLen) != 0)
149     return S_FALSE;
150   m_Stream = inStream;
151   return S_OK;
152 }
153 
RemoveTailSpaces(char * dest,const char * s,unsigned size)154 static unsigned RemoveTailSpaces(char *dest, const char *s, unsigned size)
155 {
156   memcpy(dest, s, size);
157   for (; size != 0; size--)
158   {
159     if (dest[size - 1] != ' ')
160       break;
161   }
162   dest[size] = 0;
163   return size;
164 }
165 
OctalToNumber32(const char * s,unsigned size,UInt32 & res)166 static bool OctalToNumber32(const char *s, unsigned size, UInt32 &res)
167 {
168   res = 0;
169   char sz[32];
170   size = RemoveTailSpaces(sz, s, size);
171   if (size == 0 || strcmp(sz, "-1") == 0)
172     return true; // some items don't contain any numbers
173   const char *end;
174   UInt64 res64 = ConvertOctStringToUInt64(sz, &end);
175   if ((unsigned)(end - sz) != size)
176     return false;
177   res = (UInt32)res64;
178   return (res64 <= 0xFFFFFFFF);
179 }
180 
DecimalToNumber(const char * s,unsigned size,UInt64 & res)181 static bool DecimalToNumber(const char *s, unsigned size, UInt64 &res)
182 {
183   res = 0;
184   char sz[32];
185   size = RemoveTailSpaces(sz, s, size);
186   if (size == 0 || strcmp(sz, "-1") == 0)
187     return true; // some items don't contain any numbers
188   const char *end;
189   res = ConvertStringToUInt64(sz, &end);
190   return ((unsigned)(end - sz) == size);
191 }
192 
DecimalToNumber32(const char * s,unsigned size,UInt32 & res)193 static bool DecimalToNumber32(const char *s, unsigned size, UInt32 &res)
194 {
195   UInt64 res64;
196   if (!DecimalToNumber(s, size, res64))
197     return false;
198   res = (UInt32)res64;
199   return (res64 <= 0xFFFFFFFF);
200 }
201 
202 #define RIF(x) { if (!(x)) return S_FALSE; }
203 
204 
GetNextItem(CItem & item,bool & filled)205 HRESULT CInArchive::GetNextItem(CItem &item, bool &filled)
206 {
207   filled = false;
208 
209   char header[kHeaderSize];
210   const char *cur = header;
211 
212   {
213     size_t processedSize = sizeof(header);
214     item.HeaderPos = Position;
215     item.HeaderSize = kHeaderSize;
216     RINOK(ReadStream(m_Stream, header, &processedSize))
217     if (processedSize != sizeof(header))
218       return S_OK;
219     if (header[kHeaderSize - 2] != 0x60 ||
220       header[kHeaderSize - 1] != 0x0A)
221       return S_OK;
222     for (unsigned i = 0; i < kHeaderSize - 2; i++)
223       // if (header[i] < 0x20)
224       if (header[i] == 0)
225         return S_OK;
226     Position += processedSize;
227   }
228 
229   UInt32 longNameLen = 0;
230   if (cur[0] == '#' &&
231       cur[1] == '1' &&
232       cur[2] == '/' &&
233       cur[3] != 0)
234   {
235     // BSD variant
236     RIF(DecimalToNumber32(cur + 3, kNameSize - 3 , longNameLen))
237     if (longNameLen >= (1 << 12))
238       longNameLen = 0;
239   }
240   else
241   {
242     char tempString[kNameSize + 1];
243     RemoveTailSpaces(tempString, cur, kNameSize);
244     item.Name = tempString;
245   }
246   cur += kNameSize;
247 
248   RIF(DecimalToNumber32(cur, kTimeSize, item.MTime)) cur += kTimeSize;
249   RIF(DecimalToNumber32(cur, kUserSize, item.User)) cur += kUserSize;
250   RIF(DecimalToNumber32(cur, kUserSize, item.Group)) cur += kUserSize;
251   RIF(OctalToNumber32(cur, kModeSize, item.Mode)) cur += kModeSize;
252   RIF(DecimalToNumber(cur, kSizeSize, item.Size)) cur += kSizeSize;
253 
254   if (longNameLen != 0 && longNameLen <= item.Size)
255   {
256     SubType = kSubType_BSD;
257     size_t processedSize = longNameLen;
258     char *s = item.Name.GetBuf(longNameLen);
259     HRESULT res = ReadStream(m_Stream, s, &processedSize);
260     item.Name.ReleaseBuf_CalcLen(longNameLen);
261     RINOK(res)
262     if (processedSize != longNameLen)
263       return S_OK;
264     item.Size -= longNameLen;
265     item.HeaderSize += longNameLen;
266     Position += processedSize;
267   }
268 
269   filled = true;
270   return S_OK;
271 }
272 
273 
274 Z7_CLASS_IMP_CHandler_IInArchive_1(
275   IInArchiveGetStream
276 )
277   bool _isArc;
278   CObjectVector<CItem> _items;
279   CMyComPtr<IInStream> _stream;
280   UInt64 _phySize;
281   Int32 _mainSubfile;
282 
283   EType _type;
284   ESubType _subType;
285   int _longNames_FileIndex;
286   unsigned _numLibFiles;
287   AString _errorMessage;
288   AString _libFiles[2];
289 
290   void UpdateErrorMessage(const char *s);
291 
292   HRESULT ParseLongNames(IInStream *stream);
293   void ChangeDuplicateNames();
294   int FindItem(UInt32 offset) const;
295   HRESULT AddFunc(UInt32 offset, const Byte *data, size_t size, size_t &pos);
296   HRESULT ParseLibSymbols(IInStream *stream, unsigned fileIndex);
297 };
298 
UpdateErrorMessage(const char * s)299 void CHandler::UpdateErrorMessage(const char *s)
300 {
301   if (!_errorMessage.IsEmpty())
302     _errorMessage.Add_LF();
303   _errorMessage += s;
304 }
305 
306 static const Byte kArcProps[] =
307 {
308   kpidSubType
309 };
310 
311 static const Byte kProps[] =
312 {
313   kpidPath,
314   kpidSize,
315   kpidMTime,
316   kpidPosixAttrib,
317   kpidUserId,
318   kpidGroupId
319 };
320 
321 IMP_IInArchive_Props
322 IMP_IInArchive_ArcProps
323 
ParseLongNames(IInStream * stream)324 HRESULT CHandler::ParseLongNames(IInStream *stream)
325 {
326   unsigned i;
327   for (i = 0; i < _items.Size(); i++)
328     if (_items[i].Name == "//")
329       break;
330   if (i == _items.Size())
331     return S_OK;
332 
333   unsigned fileIndex = i;
334   const CItem &item = _items[fileIndex];
335   if (item.Size > ((UInt32)1 << 30))
336     return S_FALSE;
337   RINOK(InStream_SeekSet(stream, item.GetDataPos()))
338   const size_t size = (size_t)item.Size;
339 
340   CByteArr p(size);
341   RINOK(ReadStream_FALSE(stream, p, size))
342 
343   for (i = 0; i < _items.Size(); i++)
344   {
345     CItem &item2 = _items[i];
346     if (item2.Name[0] != '/')
347       continue;
348     const char *ptr = item2.Name.Ptr(1);
349     const char *end;
350     UInt32 pos = ConvertStringToUInt32(ptr, &end);
351     if (*end != 0 || end == ptr)
352       continue;
353     if (pos >= size)
354       continue;
355     UInt32 start = pos;
356     for (;;)
357     {
358       if (pos >= size)
359         return S_FALSE;
360       const Byte c = p[pos];
361       if (c == 0 || c == 0x0A)
362         break;
363       pos++;
364     }
365     item2.Name.SetFrom((const char *)(p + start), (unsigned)(pos - start));
366   }
367 
368   _longNames_FileIndex = (int)fileIndex;
369   return S_OK;
370 }
371 
ChangeDuplicateNames()372 void CHandler::ChangeDuplicateNames()
373 {
374   unsigned i;
375   for (i = 1; i < _items.Size(); i++)
376   {
377     CItem &item = _items[i];
378     if (item.Name[0] == '/')
379       continue;
380     CItem &prev = _items[i - 1];
381     if (item.Name == prev.Name)
382     {
383       if (prev.SameNameIndex < 0)
384         prev.SameNameIndex = 0;
385       item.SameNameIndex = prev.SameNameIndex + 1;
386     }
387   }
388   for (i = 0; i < _items.Size(); i++)
389   {
390     CItem &item = _items[i];
391     if (item.SameNameIndex < 0)
392       continue;
393     char sz[32];
394     ConvertUInt32ToString((unsigned)item.SameNameIndex + 1, sz);
395     unsigned len = MyStringLen(sz);
396     sz[len++] = '.';
397     sz[len] = 0;
398     item.Name.Insert(0, sz);
399   }
400 }
401 
FindItem(UInt32 offset) const402 int CHandler::FindItem(UInt32 offset) const
403 {
404   unsigned left = 0, right = _items.Size();
405   while (left != right)
406   {
407     const unsigned mid = (left + right) / 2;
408     const UInt64 midVal = _items[mid].HeaderPos;
409     if (offset == midVal)
410       return (int)mid;
411     if (offset < midVal)
412       right = mid;
413     else
414       left = mid + 1;
415   }
416   return -1;
417 }
418 
AddFunc(UInt32 offset,const Byte * data,size_t size,size_t & pos)419 HRESULT CHandler::AddFunc(UInt32 offset, const Byte *data, size_t size, size_t &pos)
420 {
421   const int fileIndex = FindItem(offset);
422   if (fileIndex < (int)0)
423     return S_FALSE;
424 
425   size_t i = pos;
426   do
427   {
428     if (i >= size)
429       return S_FALSE;
430   }
431   while (data[i++] != 0);
432 
433   AString &s = _libFiles[_numLibFiles];
434   const AString &name = _items[(unsigned)fileIndex].Name;
435   s += name;
436   if (!name.IsEmpty() && name.Back() == '/')
437     s.DeleteBack();
438   s += "    ";
439   s += (const char *)(data + pos);
440   // s.Add_Char((char)0xD);
441   s.Add_LF();
442   pos = i;
443   return S_OK;
444 }
445 
Get32(const Byte * p,unsigned be)446 static UInt32 Get32(const Byte *p, unsigned be) { if (be) return GetBe32(p); return GetUi32(p); }
447 
ParseLibSymbols(IInStream * stream,unsigned fileIndex)448 HRESULT CHandler::ParseLibSymbols(IInStream *stream, unsigned fileIndex)
449 {
450   CItem &item = _items[fileIndex];
451   if (item.Name != "/" &&
452       item.Name != "__.SYMDEF"  &&
453       item.Name != "__.SYMDEF SORTED")
454     return S_OK;
455   if (item.Size > ((UInt32)1 << 30) ||
456       item.Size < 4)
457     return S_OK;
458   RINOK(InStream_SeekSet(stream, item.GetDataPos()))
459   size_t size = (size_t)item.Size;
460   CByteArr p(size);
461   RINOK(ReadStream_FALSE(stream, p, size))
462 
463   size_t pos = 0;
464 
465   if (item.Name != "/")
466   {
467     // "__.SYMDEF" parsing (BSD)
468     unsigned be;
469     for (be = 0; be < 2; be++)
470     {
471       const UInt32 tableSize = Get32(p, be);
472       pos = 4;
473       if (size - pos < tableSize || (tableSize & 7) != 0)
474         continue;
475       size_t namesStart = pos + tableSize;
476       const UInt32 namesSize = Get32(p.ConstData() + namesStart, be);
477       namesStart += 4;
478       if (namesStart > size || namesStart + namesSize != size)
479         continue;
480 
481       const UInt32 numSymbols = tableSize >> 3;
482       UInt32 i;
483       for (i = 0; i < numSymbols; i++, pos += 8)
484       {
485         size_t namePos = Get32(p + pos, be);
486         const UInt32 offset = Get32(p + pos + 4, be);
487         if (AddFunc(offset, p + namesStart, namesSize, namePos) != S_OK)
488           break;
489       }
490       if (i == numSymbols)
491       {
492         pos = size;
493         _type = kType_ALib;
494         _subType = kSubType_BSD;
495         break;
496       }
497     }
498     if (be == 2)
499       return S_FALSE;
500   }
501   else if (_numLibFiles == 0)
502   {
503     // archive symbol table (GNU)
504     const UInt32 numSymbols = GetBe32(p);
505     pos = 4;
506     if (numSymbols > (size - pos) / 4)
507       return S_FALSE;
508     pos += 4 * numSymbols;
509 
510     for (UInt32 i = 0; i < numSymbols; i++)
511     {
512       const UInt32 offset = GetBe32(p + 4 + i * 4);
513       RINOK(AddFunc(offset, p, size, pos))
514     }
515     _type = kType_ALib;
516   }
517   else
518   {
519     // Second linker file (Microsoft .lib)
520     const UInt32 numMembers = GetUi32(p);
521     pos = 4;
522     if (numMembers > (size - pos) / 4)
523       return S_FALSE;
524     pos += 4 * numMembers;
525 
526     if (size - pos < 4)
527       return S_FALSE;
528     const UInt32 numSymbols = GetUi32(p + pos);
529     pos += 4;
530     if (numSymbols > (size - pos) / 2)
531       return S_FALSE;
532     size_t indexStart = pos;
533     pos += 2 * numSymbols;
534 
535     for (UInt32 i = 0; i < numSymbols; i++)
536     {
537       // index is 1-based. So 32-bit numSymbols field works as item[0]
538       const UInt32 index = GetUi16(p + indexStart + i * 2);
539       if (index == 0 || index > numMembers)
540         return S_FALSE;
541       const UInt32 offset = GetUi32(p + index * 4);
542       RINOK(AddFunc(offset, p, size, pos))
543     }
544     _type = kType_Lib;
545   }
546   // size can be 2-byte aligned in linux files
547   if (pos != size && pos + (pos & 1) != size)
548     return S_FALSE;
549   item.TextFileIndex = (int)(_numLibFiles++);
550   return S_OK;
551 }
552 
Z7_COM7F_IMF(CHandler::Open (IInStream * stream,const UInt64 *,IArchiveOpenCallback * callback))553 Z7_COM7F_IMF(CHandler::Open(IInStream *stream,
554     const UInt64 * /* maxCheckStartPosition */,
555     IArchiveOpenCallback *callback))
556 {
557   COM_TRY_BEGIN
558   {
559     Close();
560 
561     UInt64 fileSize;
562     RINOK(InStream_AtBegin_GetSize(stream, fileSize))
563 
564     CInArchive arc;
565     RINOK(arc.Open(stream))
566 
567     if (callback)
568     {
569       RINOK(callback->SetTotal(NULL, &fileSize))
570       const UInt64 numFiles = _items.Size();
571       RINOK(callback->SetCompleted(&numFiles, &arc.Position))
572     }
573 
574     CItem item;
575     for (;;)
576     {
577       bool filled;
578       RINOK(arc.GetNextItem(item, filled))
579       if (!filled)
580         break;
581       _items.Add(item);
582       arc.SkipData(item.Size);
583       if (callback && (_items.Size() & 0xFF) == 0)
584       {
585         const UInt64 numFiles = _items.Size();
586         RINOK(callback->SetCompleted(&numFiles, &arc.Position))
587       }
588     }
589 
590     if (_items.IsEmpty())
591     {
592       // we don't need false empty archives (8-bytes signature only)
593       if (arc.Position != fileSize)
594         return S_FALSE;
595     }
596 
597     _isArc = true;
598 
599     _subType = arc.SubType;
600 
601     if (ParseLongNames(stream) != S_OK)
602       UpdateErrorMessage("Long file names parsing error");
603     if (_longNames_FileIndex >= 0)
604       _items.Delete((unsigned)_longNames_FileIndex);
605 
606     if (!_items.IsEmpty() && _items[0].Name == "debian-binary")
607     {
608       _type = kType_Deb;
609       _items.DeleteFrontal(1);
610       for (unsigned i = 0; i < _items.Size(); i++)
611         if (_items[i].Name.IsPrefixedBy("data.tar."))
612         {
613           if (_mainSubfile < 0)
614             _mainSubfile = (int)i;
615           else
616           {
617             _mainSubfile = -1;
618             break;
619           }
620         }
621     }
622     else
623     {
624       ChangeDuplicateNames();
625       bool error = false;
626       for (unsigned li = 0; li < 2 && li < _items.Size(); li++)
627         if (ParseLibSymbols(stream, li) != S_OK)
628           error = true;
629       if (error)
630         UpdateErrorMessage("Library symbols information error");
631     }
632 
633     _stream = stream;
634     _phySize = arc.Position;
635 
636     /*
637     if (fileSize < _phySize)
638       UpdateErrorMessage("Unexpected end of archive");
639     */
640   }
641   return S_OK;
642   COM_TRY_END
643 }
644 
Z7_COM7F_IMF(CHandler::Close ())645 Z7_COM7F_IMF(CHandler::Close())
646 {
647   _isArc = false;
648   _phySize = 0;
649 
650   _errorMessage.Empty();
651   _stream.Release();
652   _items.Clear();
653 
654   _type = kType_Ar;
655   _subType = kSubType_None;
656   _mainSubfile = -1;
657   _longNames_FileIndex = -1;
658 
659   _numLibFiles = 0;
660   _libFiles[0].Empty();
661   _libFiles[1].Empty();
662 
663   return S_OK;
664 }
665 
Z7_COM7F_IMF(CHandler::GetNumberOfItems (UInt32 * numItems))666 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
667 {
668   *numItems = _items.Size();
669   return S_OK;
670 }
671 
Z7_COM7F_IMF(CHandler::GetArchiveProperty (PROPID propID,PROPVARIANT * value))672 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
673 {
674   COM_TRY_BEGIN
675   NCOM::CPropVariant prop;
676   switch (propID)
677   {
678     case kpidPhySize: prop = _phySize; break;
679     case kpidMainSubfile: if (_mainSubfile >= 0) prop = (UInt32)_mainSubfile; break;
680     case kpidExtension: prop = k_TypeExtionsions[(unsigned)_type]; break;
681     case kpidShortComment:
682     case kpidSubType:
683     {
684       AString s (k_TypeExtionsions[(unsigned)_type]);
685       if (_subType == kSubType_BSD)
686         s += ":BSD";
687       prop = s;
688       break;
689     }
690     case kpidErrorFlags:
691     {
692       UInt32 v = 0;
693       if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;
694       prop = v;
695       break;
696     }
697     case kpidWarning: if (!_errorMessage.IsEmpty()) prop = _errorMessage; break;
698     case kpidIsNotArcType: if (_type != kType_Deb) prop = true; break;
699   }
700   prop.Detach(value);
701   return S_OK;
702   COM_TRY_END
703 }
704 
Z7_COM7F_IMF(CHandler::GetProperty (UInt32 index,PROPID propID,PROPVARIANT * value))705 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
706 {
707   COM_TRY_BEGIN
708   NWindows::NCOM::CPropVariant prop;
709   const CItem &item = _items[index];
710   switch (propID)
711   {
712     case kpidPath:
713       if (item.TextFileIndex >= 0)
714         prop = (item.TextFileIndex == 0) ? "1.txt" : "2.txt";
715       else
716         prop = (const wchar_t *)NItemName::GetOsPath_Remove_TailSlash(MultiByteToUnicodeString(item.Name, CP_OEMCP));
717       break;
718     case kpidSize:
719     case kpidPackSize:
720       if (item.TextFileIndex >= 0)
721         prop = (UInt64)_libFiles[(unsigned)item.TextFileIndex].Len();
722       else
723         prop = item.Size;
724       break;
725     case kpidMTime:
726     {
727       if (item.MTime != 0)
728         PropVariant_SetFrom_UnixTime(prop, item.MTime);
729       break;
730     }
731     case kpidUserId: if (item.User != 0) prop = item.User; break;
732     case kpidGroupId: if (item.Group != 0) prop = item.Group; break;
733     case kpidPosixAttrib:
734       if (item.TextFileIndex < 0)
735         prop = item.Mode;
736       break;
737   }
738   prop.Detach(value);
739   return S_OK;
740   COM_TRY_END
741 }
742 
Z7_COM7F_IMF(CHandler::Extract (const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback))743 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
744     Int32 testMode, IArchiveExtractCallback *extractCallback))
745 {
746   COM_TRY_BEGIN
747   const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
748   if (allFilesMode)
749     numItems = _items.Size();
750   if (numItems == 0)
751     return S_OK;
752   UInt64 totalSize = 0;
753   UInt32 i;
754   for (i = 0; i < numItems; i++)
755   {
756     const CItem &item = _items[allFilesMode ? i : indices[i]];
757     totalSize +=
758       (item.TextFileIndex >= 0) ?
759         (UInt64)_libFiles[(unsigned)item.TextFileIndex].Len() : item.Size;
760   }
761   extractCallback->SetTotal(totalSize);
762 
763   UInt64 currentTotalSize = 0;
764 
765   CMyComPtr2_Create<ICompressCoder, NCompress::CCopyCoder> copyCoder;
766   CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps;
767   lps->Init(extractCallback, false);
768 
769   CMyComPtr2_Create<ISequentialInStream, CLimitedSequentialInStream> inStream;
770   inStream->SetStream(_stream);
771 
772   for (i = 0;; i++)
773   {
774     lps->InSize = lps->OutSize = currentTotalSize;
775     RINOK(lps->SetCur())
776     if (i >= numItems)
777       break;
778     Int32 opRes;
779    {
780     CMyComPtr<ISequentialOutStream> realOutStream;
781     const Int32 askMode = testMode ?
782         NExtract::NAskMode::kTest :
783         NExtract::NAskMode::kExtract;
784     const UInt32 index = allFilesMode ? i : indices[i];
785     const CItem &item = _items[index];
786     RINOK(extractCallback->GetStream(index, &realOutStream, askMode))
787     currentTotalSize += (item.TextFileIndex >= 0) ?
788         (UInt64)_libFiles[(unsigned)item.TextFileIndex].Len() : item.Size;
789 
790     if (!testMode && !realOutStream)
791       continue;
792     RINOK(extractCallback->PrepareOperation(askMode))
793     if (testMode)
794     {
795       RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK))
796       continue;
797     }
798     opRes = NExtract::NOperationResult::kOK;
799     if (item.TextFileIndex >= 0)
800     {
801       const AString &f = _libFiles[(unsigned)item.TextFileIndex];
802       if (realOutStream)
803         RINOK(WriteStream(realOutStream, f, f.Len()))
804     }
805     else
806     {
807       RINOK(InStream_SeekSet(_stream, item.GetDataPos()))
808       inStream->Init(item.Size);
809       RINOK(copyCoder.Interface()->Code(inStream, realOutStream, NULL, NULL, lps))
810       if (copyCoder->TotalSize != item.Size)
811         opRes = NExtract::NOperationResult::kDataError;
812     }
813    }
814     RINOK(extractCallback->SetOperationResult(opRes))
815   }
816   return S_OK;
817   COM_TRY_END
818 }
819 
Z7_COM7F_IMF(CHandler::GetStream (UInt32 index,ISequentialInStream ** stream))820 Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream))
821 {
822   COM_TRY_BEGIN
823   const CItem &item = _items[index];
824   if (item.TextFileIndex >= 0)
825   {
826     const AString &f = _libFiles[(unsigned)item.TextFileIndex];
827     Create_BufInStream_WithNewBuffer((const void *)(const char *)f, f.Len(), stream);
828     return S_OK;
829   }
830   else
831     return CreateLimitedInStream(_stream, item.GetDataPos(), item.Size, stream);
832   COM_TRY_END
833 }
834 
835 REGISTER_ARC_I(
836   "Ar", "ar a deb udeb lib", NULL, 0xEC,
837   kSignature,
838   0,
839   0,
840   NULL)
841 
842 }}
843