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