Imported Upstream version 9.20
[platform/upstream/7zip.git] / CPP / 7zip / Archive / DebHandler.cpp
1 // DebHandler.cpp\r
2 \r
3 #include "StdAfx.h"\r
4 \r
5 #include "Common/ComTry.h"\r
6 #include "Common/StringConvert.h"\r
7 #include "Common/StringToInt.h"\r
8 \r
9 #include "Windows/PropVariant.h"\r
10 #include "Windows/Time.h"\r
11 \r
12 #include "../Common/LimitedStreams.h"\r
13 #include "../Common/ProgressUtils.h"\r
14 #include "../Common/RegisterArc.h"\r
15 #include "../Common/StreamUtils.h"\r
16 \r
17 #include "../Compress/CopyCoder.h"\r
18 \r
19 #include "Common/ItemNameUtils.h"\r
20 \r
21 using namespace NWindows;\r
22 using namespace NTime;\r
23 \r
24 namespace NArchive {\r
25 namespace NDeb {\r
26 \r
27 namespace NHeader\r
28 {\r
29   const int kSignatureLen = 8;\r
30   \r
31   const char *kSignature = "!<arch>\n";\r
32 \r
33   const int kNameSize = 16;\r
34   const int kTimeSize = 12;\r
35   const int kModeSize = 8;\r
36   const int kSizeSize = 10;\r
37 \r
38   /*\r
39   struct CHeader\r
40   {\r
41     char Name[kNameSize];\r
42     char MTime[kTimeSize];\r
43     char Number0[6];\r
44     char Number1[6];\r
45     char Mode[kModeSize];\r
46     char Size[kSizeSize];\r
47     char Quote;\r
48     char NewLine;\r
49   };\r
50   */\r
51   const int kHeaderSize = kNameSize + kTimeSize + 6 + 6 + kModeSize + kSizeSize + 1 + 1;\r
52 }\r
53 \r
54 struct CItem\r
55 {\r
56   AString Name;\r
57   UInt64 Size;\r
58   UInt32 MTime;\r
59   UInt32 Mode;\r
60 \r
61   UInt64 HeaderPos;\r
62   UInt64 GetDataPos() const { return HeaderPos + NHeader::kHeaderSize; };\r
63   // UInt64 GetFullSize() const { return NFileHeader::kRecordSize + Size; };\r
64 };\r
65 \r
66 class CInArchive\r
67 {\r
68   CMyComPtr<IInStream> m_Stream;\r
69   \r
70   HRESULT GetNextItemReal(bool &filled, CItem &itemInfo);\r
71 public:\r
72   UInt64 m_Position;\r
73   HRESULT Open(IInStream *inStream);\r
74   HRESULT GetNextItem(bool &filled, CItem &itemInfo);\r
75   HRESULT SkipData(UInt64 dataSize);\r
76 };\r
77 \r
78 HRESULT CInArchive::Open(IInStream *inStream)\r
79 {\r
80   RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &m_Position));\r
81   char signature[NHeader::kSignatureLen];\r
82   RINOK(ReadStream_FALSE(inStream, signature, NHeader::kSignatureLen));\r
83   m_Position += NHeader::kSignatureLen;\r
84   if (memcmp(signature, NHeader::kSignature, NHeader::kSignatureLen) != 0)\r
85     return S_FALSE;\r
86   m_Stream = inStream;\r
87   return S_OK;\r
88 }\r
89 \r
90 static void MyStrNCpy(char *dest, const char *src, int size)\r
91 {\r
92   for (int i = 0; i < size; i++)\r
93   {\r
94     char c = src[i];\r
95     dest[i] = c;\r
96     if (c == 0)\r
97       break;\r
98   }\r
99 }\r
100 \r
101 static bool OctalToNumber(const char *s, int size, UInt64 &res)\r
102 {\r
103   char sz[32];\r
104   MyStrNCpy(sz, s, size);\r
105   sz[size] = 0;\r
106   const char *end;\r
107   int i;\r
108   for (i = 0; sz[i] == ' '; i++);\r
109   res = ConvertOctStringToUInt64(sz + i, &end);\r
110   return (*end == ' ' || *end == 0);\r
111 }\r
112 \r
113 static bool OctalToNumber32(const char *s, int size, UInt32 &res)\r
114 {\r
115   UInt64 res64;\r
116   if (!OctalToNumber(s, size, res64))\r
117     return false;\r
118   res = (UInt32)res64;\r
119   return (res64 <= 0xFFFFFFFF);\r
120 }\r
121 \r
122 static bool DecimalToNumber(const char *s, int size, UInt64 &res)\r
123 {\r
124   char sz[32];\r
125   MyStrNCpy(sz, s, size);\r
126   sz[size] = 0;\r
127   const char *end;\r
128   int i;\r
129   for (i = 0; sz[i] == ' '; i++);\r
130   res = ConvertStringToUInt64(sz + i, &end);\r
131   return (*end == ' ' || *end == 0);\r
132 }\r
133 \r
134 static bool DecimalToNumber32(const char *s, int size, UInt32 &res)\r
135 {\r
136   UInt64 res64;\r
137   if (!DecimalToNumber(s, size, res64))\r
138     return false;\r
139   res = (UInt32)res64;\r
140   return (res64 <= 0xFFFFFFFF);\r
141 }\r
142 \r
143 #define RIF(x) { if (!(x)) return S_FALSE; }\r
144 \r
145 \r
146 HRESULT CInArchive::GetNextItemReal(bool &filled, CItem &item)\r
147 {\r
148   filled = false;\r
149 \r
150   char header[NHeader::kHeaderSize];\r
151   const char *cur = header;\r
152 \r
153   size_t processedSize = sizeof(header);\r
154   item.HeaderPos = m_Position;\r
155   RINOK(ReadStream(m_Stream, header, &processedSize));\r
156   if (processedSize != sizeof(header))\r
157     return S_OK;\r
158   m_Position += processedSize;\r
159   \r
160   char tempString[NHeader::kNameSize + 1];\r
161   MyStrNCpy(tempString, cur, NHeader::kNameSize);\r
162   cur += NHeader::kNameSize;\r
163   tempString[NHeader::kNameSize] = '\0';\r
164   item.Name = tempString;\r
165   item.Name.Trim();\r
166 \r
167   for (int i = 0; i < item.Name.Length(); i++)\r
168     if (((Byte)item.Name[i]) < 0x20)\r
169       return S_FALSE;\r
170 \r
171   RIF(DecimalToNumber32(cur, NHeader::kTimeSize, item.MTime));\r
172   cur += NHeader::kTimeSize;\r
173 \r
174   cur += 6 + 6;\r
175   \r
176   RIF(OctalToNumber32(cur, NHeader::kModeSize, item.Mode));\r
177   cur += NHeader::kModeSize;\r
178 \r
179   RIF(DecimalToNumber(cur, NHeader::kSizeSize, item.Size));\r
180   cur += NHeader::kSizeSize;\r
181 \r
182   filled = true;\r
183   return S_OK;\r
184 }\r
185 \r
186 HRESULT CInArchive::GetNextItem(bool &filled, CItem &item)\r
187 {\r
188   for (;;)\r
189   {\r
190     RINOK(GetNextItemReal(filled, item));\r
191     if (!filled)\r
192       return S_OK;\r
193     if (item.Name.Compare("debian-binary") != 0)\r
194       return S_OK;\r
195     if (item.Size != 4)\r
196       return S_OK;\r
197     SkipData(item.Size);\r
198   }\r
199 }\r
200 \r
201 HRESULT CInArchive::SkipData(UInt64 dataSize)\r
202 {\r
203   return m_Stream->Seek((dataSize + 1) & (~((UInt64)0x1)), STREAM_SEEK_CUR, &m_Position);\r
204 }\r
205 \r
206 class CHandler:\r
207   public IInArchive,\r
208   public IInArchiveGetStream,\r
209   public CMyUnknownImp\r
210 {\r
211   CObjectVector<CItem> _items;\r
212   CMyComPtr<IInStream> _stream;\r
213   Int32 _mainSubfile;\r
214   UInt64 _phySize;\r
215 public:\r
216   MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream)\r
217   INTERFACE_IInArchive(;)\r
218   STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);\r
219 };\r
220 \r
221 static STATPROPSTG kArcProps[] =\r
222 {\r
223   { NULL, kpidPhySize, VT_UI8}\r
224 };\r
225 \r
226 static STATPROPSTG kProps[] =\r
227 {\r
228   { NULL, kpidPath, VT_BSTR},\r
229   { NULL, kpidSize, VT_UI8},\r
230   { NULL, kpidMTime, VT_FILETIME}\r
231 };\r
232 \r
233 IMP_IInArchive_Props\r
234 IMP_IInArchive_ArcProps\r
235 \r
236 STDMETHODIMP CHandler::Open(IInStream *stream,\r
237     const UInt64 * /* maxCheckStartPosition */,\r
238     IArchiveOpenCallback *openArchiveCallback)\r
239 {\r
240   COM_TRY_BEGIN\r
241   {\r
242     _mainSubfile = -1;\r
243     CInArchive archive;\r
244     if (archive.Open(stream) != S_OK)\r
245       return S_FALSE;\r
246     _items.Clear();\r
247 \r
248     if (openArchiveCallback != NULL)\r
249     {\r
250       RINOK(openArchiveCallback->SetTotal(NULL, NULL));\r
251       UInt64 numFiles = _items.Size();\r
252       RINOK(openArchiveCallback->SetCompleted(&numFiles, NULL));\r
253     }\r
254 \r
255     for (;;)\r
256     {\r
257       CItem item;\r
258       bool filled;\r
259       HRESULT result = archive.GetNextItem(filled, item);\r
260       if (result == S_FALSE)\r
261         return S_FALSE;\r
262       if (result != S_OK)\r
263         return S_FALSE;\r
264       if (!filled)\r
265         break;\r
266       if (item.Name.Left(5) == "data.")\r
267         _mainSubfile = _items.Size();\r
268       _items.Add(item);\r
269       archive.SkipData(item.Size);\r
270       if (openArchiveCallback != NULL)\r
271       {\r
272         UInt64 numFiles = _items.Size();\r
273         RINOK(openArchiveCallback->SetCompleted(&numFiles, NULL));\r
274       }\r
275     }\r
276     _stream = stream;\r
277     _phySize = archive.m_Position;\r
278   }\r
279   return S_OK;\r
280   COM_TRY_END\r
281 }\r
282 \r
283 STDMETHODIMP CHandler::Close()\r
284 {\r
285   _stream.Release();\r
286   _items.Clear();\r
287   return S_OK;\r
288 }\r
289 \r
290 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)\r
291 {\r
292   *numItems = _items.Size();\r
293   return S_OK;\r
294 }\r
295 \r
296 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)\r
297 {\r
298   NCOM::CPropVariant prop;\r
299   switch(propID)\r
300   {\r
301     case kpidPhySize: prop = _phySize; break;\r
302     case kpidMainSubfile: if (_mainSubfile >= 0) prop = (UInt32)_mainSubfile; break;\r
303   }\r
304   prop.Detach(value);\r
305   return S_OK;\r
306 }\r
307 \r
308 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)\r
309 {\r
310   COM_TRY_BEGIN\r
311   NWindows::NCOM::CPropVariant prop;\r
312   const CItem &item = _items[index];\r
313 \r
314   switch(propID)\r
315   {\r
316     case kpidPath: prop = (const wchar_t *)NItemName::GetOSName2(MultiByteToUnicodeString(item.Name, CP_OEMCP)); break;\r
317     case kpidSize:\r
318     case kpidPackSize:\r
319       prop = item.Size;\r
320       break;\r
321     case kpidMTime:\r
322     {\r
323       if (item.MTime != 0)\r
324       {\r
325         FILETIME fileTime;\r
326         NTime::UnixTimeToFileTime(item.MTime, fileTime);\r
327         prop = fileTime;\r
328       }\r
329       break;\r
330     }\r
331   }\r
332   prop.Detach(value);\r
333   return S_OK;\r
334   COM_TRY_END\r
335 }\r
336 \r
337 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,\r
338     Int32 testMode, IArchiveExtractCallback *extractCallback)\r
339 {\r
340   COM_TRY_BEGIN\r
341   bool allFilesMode = (numItems == (UInt32)-1);\r
342   if (allFilesMode)\r
343     numItems = _items.Size();\r
344   if (numItems == 0)\r
345     return S_OK;\r
346   UInt64 totalSize = 0;\r
347   UInt32 i;\r
348   for (i = 0; i < numItems; i++)\r
349     totalSize += _items[allFilesMode ? i : indices[i]].Size;\r
350   extractCallback->SetTotal(totalSize);\r
351 \r
352   UInt64 currentTotalSize = 0;\r
353   \r
354   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();\r
355   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;\r
356 \r
357   CLocalProgress *lps = new CLocalProgress;\r
358   CMyComPtr<ICompressProgressInfo> progress = lps;\r
359   lps->Init(extractCallback, false);\r
360 \r
361   CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;\r
362   CMyComPtr<ISequentialInStream> inStream(streamSpec);\r
363   streamSpec->SetStream(_stream);\r
364 \r
365   for (i = 0; i < numItems; i++)\r
366   {\r
367     lps->InSize = lps->OutSize = currentTotalSize;\r
368     RINOK(lps->SetCur());\r
369     CMyComPtr<ISequentialOutStream> realOutStream;\r
370     Int32 askMode = testMode ?\r
371         NExtract::NAskMode::kTest :\r
372         NExtract::NAskMode::kExtract;\r
373     Int32 index = allFilesMode ? i : indices[i];\r
374     const CItem &item = _items[index];\r
375     RINOK(extractCallback->GetStream(index, &realOutStream, askMode));\r
376     currentTotalSize += item.Size;\r
377     \r
378     if (!testMode && !realOutStream)\r
379       continue;\r
380     RINOK(extractCallback->PrepareOperation(askMode));\r
381     if (testMode)\r
382     {\r
383       RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));\r
384       continue;\r
385     }\r
386     RINOK(_stream->Seek(item.GetDataPos(), STREAM_SEEK_SET, NULL));\r
387     streamSpec->Init(item.Size);\r
388     RINOK(copyCoder->Code(inStream, realOutStream, NULL, NULL, progress));\r
389     realOutStream.Release();\r
390     RINOK(extractCallback->SetOperationResult((copyCoderSpec->TotalSize == item.Size) ?\r
391         NExtract::NOperationResult::kOK:\r
392         NExtract::NOperationResult::kDataError));\r
393   }\r
394   return S_OK;\r
395   COM_TRY_END\r
396 }\r
397 \r
398 STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)\r
399 {\r
400   COM_TRY_BEGIN\r
401   const CItem &item = _items[index];\r
402   return CreateLimitedInStream(_stream, item.GetDataPos(), item.Size, stream);\r
403   COM_TRY_END\r
404 }\r
405 \r
406 static IInArchive *CreateArc() { return new NArchive::NDeb::CHandler; }\r
407 \r
408 static CArcInfo g_ArcInfo =\r
409   { L"Deb", L"deb", 0, 0xEC, { '!', '<', 'a', 'r', 'c', 'h', '>', '\n' }, 8, false, CreateArc, 0 };\r
410 \r
411 REGISTER_ARC(Deb)\r
412 \r
413 }}\r