5 #include "Common/ComTry.h"
\r
6 #include "Common/StringConvert.h"
\r
8 #include "Windows/PropVariant.h"
\r
9 #include "Windows/Time.h"
\r
11 #include "../../Common/LimitedStreams.h"
\r
12 #include "../../Common/ProgressUtils.h"
\r
13 #include "../../Common/StreamObjects.h"
\r
14 #include "../../Common/StreamUtils.h"
\r
16 #include "../Common/ItemNameUtils.h"
\r
18 #include "TarHandler.h"
\r
21 using namespace NWindows;
\r
23 namespace NArchive {
\r
26 static const char *kUnexpectedEnd = "Unexpected end of archive";
\r
28 static const STATPROPSTG kProps[] =
\r
30 { NULL, kpidPath, VT_BSTR},
\r
31 { NULL, kpidIsDir, VT_BOOL},
\r
32 { NULL, kpidSize, VT_UI8},
\r
33 { NULL, kpidPackSize, VT_UI8},
\r
34 { NULL, kpidMTime, VT_FILETIME},
\r
35 { NULL, kpidPosixAttrib, VT_UI4},
\r
36 { NULL, kpidUser, VT_BSTR},
\r
37 { NULL, kpidGroup, VT_BSTR},
\r
38 { NULL, kpidLink, VT_BSTR}
\r
41 static const STATPROPSTG kArcProps[] =
\r
43 { NULL, kpidPhySize, VT_UI8},
\r
44 { NULL, kpidHeadersSize, VT_UI8}
\r
47 IMP_IInArchive_Props
\r
48 IMP_IInArchive_ArcProps
\r
50 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
\r
52 NCOM::CPropVariant prop;
\r
55 case kpidPhySize: if (_phySizeDefined) prop = _phySize; break;
\r
56 case kpidHeadersSize: if (_phySizeDefined) prop = _headersSize; break;
\r
57 case kpidError: if (!_errorMessage.IsEmpty()) prop = _errorMessage; break;
\r
63 HRESULT CHandler::ReadItem2(ISequentialInStream *stream, bool &filled, CItemEx &item)
\r
65 item.HeaderPos = _phySize;
\r
66 RINOK(ReadItem(stream, filled, item, _errorMessage));
\r
67 _phySize += item.HeaderSize;
\r
68 _headersSize += item.HeaderSize;
\r
72 HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
\r
76 RINOK(stream->Seek(0, STREAM_SEEK_END, &endPos));
\r
77 RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL));
\r
80 _phySizeDefined = true;
\r
85 RINOK(ReadItem2(stream, filled, item));
\r
90 RINOK(stream->Seek(item.GetPackSize(), STREAM_SEEK_CUR, &_phySize));
\r
91 if (_phySize > endPos)
\r
93 _errorMessage = kUnexpectedEnd;
\r
97 if (_phySize == endPos)
\r
99 _errorMessage = "There are no trailing zero-filled records";
\r
103 if (callback != NULL)
\r
105 if (_items.Size() == 1)
\r
107 RINOK(callback->SetTotal(NULL, &endPos));
\r
109 if (_items.Size() % 100 == 0)
\r
111 UInt64 numFiles = _items.Size();
\r
112 RINOK(callback->SetCompleted(&numFiles, &_phySize));
\r
117 if (_items.Size() == 0)
\r
119 CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback;
\r
122 callback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&openVolumeCallback);
\r
123 if (!openVolumeCallback)
\r
125 NCOM::CPropVariant prop;
\r
126 if (openVolumeCallback->GetProperty(kpidName, &prop) != S_OK)
\r
128 if (prop.vt != VT_BSTR)
\r
130 UString baseName = prop.bstrVal;
\r
131 baseName = baseName.Right(4);
\r
132 if (baseName.CompareNoCase(L".tar") != 0)
\r
138 STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *openArchiveCallback)
\r
143 RINOK(Open2(stream, openArchiveCallback));
\r
150 STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream)
\r
153 _seqStream = stream;
\r
157 STDMETHODIMP CHandler::Close()
\r
159 _errorMessage.Empty();
\r
160 _phySizeDefined = false;
\r
164 _latestIsRead = false;
\r
166 _seqStream.Release();
\r
171 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
\r
173 *numItems = (_stream ? _items.Size() : (UInt32)(Int32)-1);
\r
177 CHandler::CHandler()
\r
179 copyCoderSpec = new NCompress::CCopyCoder();
\r
180 copyCoder = copyCoderSpec;
\r
183 HRESULT CHandler::SkipTo(UInt32 index)
\r
185 while (_curIndex < index || !_latestIsRead)
\r
189 UInt64 packSize = _latestItem.GetPackSize();
\r
190 RINOK(copyCoderSpec->Code(_seqStream, NULL, &packSize, &packSize, NULL));
\r
191 _phySize += copyCoderSpec->TotalSize;
\r
192 if (copyCoderSpec->TotalSize != packSize)
\r
194 _errorMessage = kUnexpectedEnd;
\r
197 _latestIsRead = false;
\r
203 RINOK(ReadItem2(_seqStream, filled, _latestItem));
\r
206 _phySizeDefined = true;
\r
207 return E_INVALIDARG;
\r
209 _latestIsRead = true;
\r
215 static UString TarStringToUnicode(const AString &s)
\r
217 return MultiByteToUnicodeString(s, CP_OEMCP);
\r
220 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
\r
223 NWindows::NCOM::CPropVariant prop;
\r
225 const CItemEx *item;
\r
227 item = &_items[index];
\r
230 if (index < _curIndex)
\r
231 return E_INVALIDARG;
\r
234 RINOK(SkipTo(index));
\r
235 item = &_latestItem;
\r
241 case kpidPath: prop = NItemName::GetOSName2(TarStringToUnicode(item->Name)); break;
\r
242 case kpidIsDir: prop = item->IsDir(); break;
\r
243 case kpidSize: prop = item->GetUnpackSize(); break;
\r
244 case kpidPackSize: prop = item->GetPackSize(); break;
\r
246 if (item->MTime != 0)
\r
249 NTime::UnixTimeToFileTime(item->MTime, ft);
\r
253 case kpidPosixAttrib: prop = item->Mode; break;
\r
254 case kpidUser: prop = TarStringToUnicode(item->User); break;
\r
255 case kpidGroup: prop = TarStringToUnicode(item->Group); break;
\r
256 case kpidLink: prop = TarStringToUnicode(item->LinkName); break;
\r
258 prop.Detach(value);
\r
263 HRESULT CHandler::Extract(const UInt32 *indices, UInt32 numItems,
\r
264 Int32 testMode, IArchiveExtractCallback *extractCallback)
\r
267 ISequentialInStream *stream = _seqStream;
\r
268 bool seqMode = (_stream == NULL);
\r
272 bool allFilesMode = (numItems == (UInt32)-1);
\r
274 numItems = _items.Size();
\r
275 if (_stream && numItems == 0)
\r
277 UInt64 totalSize = 0;
\r
279 for (i = 0; i < numItems; i++)
\r
280 totalSize += _items[allFilesMode ? i : indices[i]].GetUnpackSize();
\r
281 extractCallback->SetTotal(totalSize);
\r
283 UInt64 totalPackSize;
\r
284 totalSize = totalPackSize = 0;
\r
286 CLocalProgress *lps = new CLocalProgress;
\r
287 CMyComPtr<ICompressProgressInfo> progress = lps;
\r
288 lps->Init(extractCallback, false);
\r
290 CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
\r
291 CMyComPtr<ISequentialInStream> inStream(streamSpec);
\r
292 streamSpec->SetStream(stream);
\r
294 CLimitedSequentialOutStream *outStreamSpec = new CLimitedSequentialOutStream;
\r
295 CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
\r
297 for (i = 0; i < numItems || seqMode; i++)
\r
299 lps->InSize = totalPackSize;
\r
300 lps->OutSize = totalSize;
\r
301 RINOK(lps->SetCur());
\r
302 CMyComPtr<ISequentialOutStream> realOutStream;
\r
303 Int32 askMode = testMode ?
\r
304 NExtract::NAskMode::kTest :
\r
305 NExtract::NAskMode::kExtract;
\r
306 Int32 index = allFilesMode ? i : indices[i];
\r
307 const CItemEx *item;
\r
310 HRESULT res = SkipTo(index);
\r
311 if (res == E_INVALIDARG)
\r
314 item = &_latestItem;
\r
317 item = &_items[index];
\r
319 RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
\r
320 UInt64 unpackSize = item->GetUnpackSize();
\r
321 totalSize += unpackSize;
\r
322 totalPackSize += item->GetPackSize();
\r
325 RINOK(extractCallback->PrepareOperation(askMode));
\r
326 RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
\r
329 bool skipMode = false;
\r
330 if (!testMode && !realOutStream)
\r
335 askMode = NExtract::NAskMode::kSkip;
\r
337 RINOK(extractCallback->PrepareOperation(askMode));
\r
339 outStreamSpec->SetStream(realOutStream);
\r
340 realOutStream.Release();
\r
341 outStreamSpec->Init(skipMode ? 0 : unpackSize, true);
\r
343 if (item->IsLink())
\r
345 RINOK(WriteStream(outStreamSpec, (const char *)item->LinkName, item->LinkName.Length()));
\r
351 RINOK(_stream->Seek(item->GetDataPosition(), STREAM_SEEK_SET, NULL));
\r
353 streamSpec->Init(item->GetPackSize());
\r
354 RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress));
\r
358 _latestIsRead = false;
\r
361 outStreamSpec->ReleaseStream();
\r
362 RINOK(extractCallback->SetOperationResult(outStreamSpec->GetRem() == 0 ?
\r
363 NExtract::NOperationResult::kOK:
\r
364 NExtract::NOperationResult::kDataError));
\r
370 STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
\r
373 const CItemEx &item = _items[index];
\r
376 CBufInStream *streamSpec = new CBufInStream;
\r
377 CMyComPtr<IInStream> streamTemp = streamSpec;
\r
378 streamSpec->Init((const Byte *)(const char *)item.LinkName, item.LinkName.Length(), (IInArchive *)this);
\r
379 *stream = streamTemp.Detach();
\r
382 return CreateLimitedInStream(_stream, item.GetDataPosition(), item.Size, stream);
\r