Imported Upstream version 9.20
[platform/upstream/7zip.git] / CPP / 7zip / Common / FileStreams.cpp
1 // FileStreams.cpp\r
2 \r
3 #include "StdAfx.h"\r
4 \r
5 #ifndef _WIN32\r
6 #include <fcntl.h>\r
7 #include <unistd.h>\r
8 #include <errno.h>\r
9 #endif\r
10 \r
11 #ifdef SUPPORT_DEVICE_FILE\r
12 #include "../../../C/Alloc.h"\r
13 #include "../../Common/Defs.h"\r
14 #endif\r
15 \r
16 #include "FileStreams.h"\r
17 \r
18 static inline HRESULT ConvertBoolToHRESULT(bool result)\r
19 {\r
20   #ifdef _WIN32\r
21   if (result)\r
22     return S_OK;\r
23   DWORD lastError = ::GetLastError();\r
24   if (lastError == 0)\r
25     return E_FAIL;\r
26   return HRESULT_FROM_WIN32(lastError);\r
27   #else\r
28   return result ? S_OK: E_FAIL;\r
29   #endif\r
30 }\r
31 \r
32 bool CInFileStream::Open(LPCTSTR fileName)\r
33 {\r
34   return File.Open(fileName);\r
35 }\r
36 \r
37 #ifdef USE_WIN_FILE\r
38 #ifndef _UNICODE\r
39 bool CInFileStream::Open(LPCWSTR fileName)\r
40 {\r
41   return File.Open(fileName);\r
42 }\r
43 #endif\r
44 #endif\r
45 \r
46 bool CInFileStream::OpenShared(LPCTSTR fileName, bool shareForWrite)\r
47 {\r
48   return File.OpenShared(fileName, shareForWrite);\r
49 }\r
50 \r
51 #ifdef USE_WIN_FILE\r
52 #ifndef _UNICODE\r
53 bool CInFileStream::OpenShared(LPCWSTR fileName, bool shareForWrite)\r
54 {\r
55   return File.OpenShared(fileName, shareForWrite);\r
56 }\r
57 #endif\r
58 #endif\r
59 \r
60 #ifdef SUPPORT_DEVICE_FILE\r
61 \r
62 static const UInt32 kClusterSize = 1 << 18;\r
63 CInFileStream::CInFileStream():\r
64   VirtPos(0),\r
65   PhyPos(0),\r
66   Buffer(0),\r
67   BufferSize(0)\r
68 {\r
69 }\r
70 \r
71 #endif\r
72 \r
73 CInFileStream::~CInFileStream()\r
74 {\r
75   #ifdef SUPPORT_DEVICE_FILE\r
76   MidFree(Buffer);\r
77   #endif\r
78 }\r
79 \r
80 STDMETHODIMP CInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize)\r
81 {\r
82   #ifdef USE_WIN_FILE\r
83   \r
84   #ifdef SUPPORT_DEVICE_FILE\r
85   if (processedSize != NULL)\r
86     *processedSize = 0;\r
87   if (size == 0)\r
88     return S_OK;\r
89   if (File.IsDeviceFile)\r
90   {\r
91     if (File.LengthDefined)\r
92     {\r
93       if (VirtPos >= File.Length)\r
94         return VirtPos == File.Length ? S_OK : E_FAIL;\r
95       UInt64 rem = File.Length - VirtPos;\r
96       if (size > rem)\r
97         size = (UInt32)rem;\r
98     }\r
99     for (;;)\r
100     {\r
101       const UInt32 mask = kClusterSize - 1;\r
102       UInt64 mask2 = ~(UInt64)mask;\r
103       UInt64 alignedPos = VirtPos & mask2;\r
104       if (BufferSize > 0 && BufferStartPos == alignedPos)\r
105       {\r
106         UInt32 pos = (UInt32)VirtPos & mask;\r
107         if (pos >= BufferSize)\r
108           return S_OK;\r
109         UInt32 rem = MyMin(BufferSize - pos, size);\r
110         memcpy(data, Buffer + pos, rem);\r
111         VirtPos += rem;\r
112         if (processedSize != NULL)\r
113           *processedSize += rem;\r
114         return S_OK;\r
115       }\r
116       \r
117       bool useBuffer = false;\r
118       if ((VirtPos & mask) != 0 || ((ptrdiff_t)data & mask) != 0 )\r
119         useBuffer = true;\r
120       else\r
121       {\r
122         UInt64 end = VirtPos + size;\r
123         if ((end & mask) != 0)\r
124         {\r
125           end &= mask2;\r
126           if (end <= VirtPos)\r
127             useBuffer = true;\r
128           else\r
129             size = (UInt32)(end - VirtPos);\r
130         }\r
131       }\r
132       if (!useBuffer)\r
133         break;\r
134       if (alignedPos != PhyPos)\r
135       {\r
136         UInt64 realNewPosition;\r
137         bool result = File.Seek(alignedPos, FILE_BEGIN, realNewPosition);\r
138         if (!result)\r
139           return ConvertBoolToHRESULT(result);\r
140         PhyPos = realNewPosition;\r
141       }\r
142 \r
143       BufferStartPos = alignedPos;\r
144       UInt32 readSize = kClusterSize;\r
145       if (File.LengthDefined)\r
146         readSize = (UInt32)MyMin(File.Length - PhyPos, (UInt64)kClusterSize);\r
147 \r
148       if (Buffer == 0)\r
149       {\r
150         Buffer = (Byte *)MidAlloc(kClusterSize);\r
151         if (Buffer == 0)\r
152           return E_OUTOFMEMORY;\r
153       }\r
154       bool result = File.Read1(Buffer, readSize, BufferSize);\r
155       if (!result)\r
156         return ConvertBoolToHRESULT(result);\r
157 \r
158       if (BufferSize == 0)\r
159         return S_OK;\r
160       PhyPos += BufferSize;\r
161     }\r
162 \r
163     if (VirtPos != PhyPos)\r
164     {\r
165       UInt64 realNewPosition;\r
166       bool result = File.Seek(VirtPos, FILE_BEGIN, realNewPosition);\r
167       if (!result)\r
168         return ConvertBoolToHRESULT(result);\r
169       PhyPos = VirtPos = realNewPosition;\r
170     }\r
171   }\r
172   #endif\r
173 \r
174   UInt32 realProcessedSize;\r
175   bool result = File.ReadPart(data, size, realProcessedSize);\r
176   if (processedSize != NULL)\r
177     *processedSize = realProcessedSize;\r
178   #ifdef SUPPORT_DEVICE_FILE\r
179   VirtPos += realProcessedSize;\r
180   PhyPos += realProcessedSize;\r
181   #endif\r
182   return ConvertBoolToHRESULT(result);\r
183   \r
184   #else\r
185   \r
186   if (processedSize != NULL)\r
187     *processedSize = 0;\r
188   ssize_t res = File.Read(data, (size_t)size);\r
189   if (res == -1)\r
190     return E_FAIL;\r
191   if (processedSize != NULL)\r
192     *processedSize = (UInt32)res;\r
193   return S_OK;\r
194 \r
195   #endif\r
196 }\r
197 \r
198 #ifdef UNDER_CE\r
199 STDMETHODIMP CStdInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize)\r
200 {\r
201   size_t s2 = fread(data, 1, size, stdout);\r
202   if (processedSize != 0)\r
203     *processedSize = s2;\r
204   return (s2 = size) ? S_OK : E_FAIL;\r
205 }\r
206 #else\r
207 STDMETHODIMP CStdInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize)\r
208 {\r
209   #ifdef _WIN32\r
210   \r
211   DWORD realProcessedSize;\r
212   UInt32 sizeTemp = (1 << 20);\r
213   if (sizeTemp > size)\r
214     sizeTemp = size;\r
215   BOOL res = ::ReadFile(GetStdHandle(STD_INPUT_HANDLE), data, sizeTemp, &realProcessedSize, NULL);\r
216   if (processedSize != NULL)\r
217     *processedSize = realProcessedSize;\r
218   if (res == FALSE && GetLastError() == ERROR_BROKEN_PIPE)\r
219     return S_OK;\r
220   return ConvertBoolToHRESULT(res != FALSE);\r
221   \r
222   #else\r
223 \r
224   if (processedSize != NULL)\r
225     *processedSize = 0;\r
226   ssize_t res;\r
227   do\r
228   {\r
229     res = read(0, data, (size_t)size);\r
230   }\r
231   while (res < 0 && (errno == EINTR));\r
232   if (res == -1)\r
233     return E_FAIL;\r
234   if (processedSize != NULL)\r
235     *processedSize = (UInt32)res;\r
236   return S_OK;\r
237   \r
238   #endif\r
239 }\r
240   \r
241 #endif\r
242 \r
243 STDMETHODIMP CInFileStream::Seek(Int64 offset, UInt32 seekOrigin,\r
244     UInt64 *newPosition)\r
245 {\r
246   if (seekOrigin >= 3)\r
247     return STG_E_INVALIDFUNCTION;\r
248 \r
249   #ifdef USE_WIN_FILE\r
250 \r
251   #ifdef SUPPORT_DEVICE_FILE\r
252   if (File.IsDeviceFile)\r
253   {\r
254     UInt64 newVirtPos = offset;\r
255     switch(seekOrigin)\r
256     {\r
257       case STREAM_SEEK_SET: break;\r
258       case STREAM_SEEK_CUR: newVirtPos += VirtPos; break;\r
259       case STREAM_SEEK_END: newVirtPos += File.Length; break;\r
260       default: return STG_E_INVALIDFUNCTION;\r
261     }\r
262     VirtPos = newVirtPos;\r
263     if (newPosition)\r
264       *newPosition = newVirtPos;\r
265     return S_OK;\r
266   }\r
267   #endif\r
268   \r
269   UInt64 realNewPosition;\r
270   bool result = File.Seek(offset, seekOrigin, realNewPosition);\r
271   \r
272   #ifdef SUPPORT_DEVICE_FILE\r
273   PhyPos = VirtPos = realNewPosition;\r
274   #endif\r
275 \r
276   if (newPosition != NULL)\r
277     *newPosition = realNewPosition;\r
278   return ConvertBoolToHRESULT(result);\r
279   \r
280   #else\r
281   \r
282   off_t res = File.Seek(offset, seekOrigin);\r
283   if (res == -1)\r
284     return E_FAIL;\r
285   if (newPosition != NULL)\r
286     *newPosition = (UInt64)res;\r
287   return S_OK;\r
288   \r
289   #endif\r
290 }\r
291 \r
292 STDMETHODIMP CInFileStream::GetSize(UInt64 *size)\r
293 {\r
294   return ConvertBoolToHRESULT(File.GetLength(*size));\r
295 }\r
296 \r
297 \r
298 //////////////////////////\r
299 // COutFileStream\r
300 \r
301 HRESULT COutFileStream::Close()\r
302 {\r
303   return ConvertBoolToHRESULT(File.Close());\r
304 }\r
305 \r
306 STDMETHODIMP COutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize)\r
307 {\r
308   #ifdef USE_WIN_FILE\r
309 \r
310   UInt32 realProcessedSize;\r
311   bool result = File.WritePart(data, size, realProcessedSize);\r
312   ProcessedSize += realProcessedSize;\r
313   if (processedSize != NULL)\r
314     *processedSize = realProcessedSize;\r
315   return ConvertBoolToHRESULT(result);\r
316   \r
317   #else\r
318   \r
319   if (processedSize != NULL)\r
320     *processedSize = 0;\r
321   ssize_t res = File.Write(data, (size_t)size);\r
322   if (res == -1)\r
323     return E_FAIL;\r
324   if (processedSize != NULL)\r
325     *processedSize = (UInt32)res;\r
326   ProcessedSize += res;\r
327   return S_OK;\r
328   \r
329   #endif\r
330 }\r
331   \r
332 STDMETHODIMP COutFileStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)\r
333 {\r
334   if (seekOrigin >= 3)\r
335     return STG_E_INVALIDFUNCTION;\r
336   #ifdef USE_WIN_FILE\r
337 \r
338   UInt64 realNewPosition;\r
339   bool result = File.Seek(offset, seekOrigin, realNewPosition);\r
340   if (newPosition != NULL)\r
341     *newPosition = realNewPosition;\r
342   return ConvertBoolToHRESULT(result);\r
343   \r
344   #else\r
345   \r
346   off_t res = File.Seek(offset, seekOrigin);\r
347   if (res == -1)\r
348     return E_FAIL;\r
349   if (newPosition != NULL)\r
350     *newPosition = (UInt64)res;\r
351   return S_OK;\r
352   \r
353   #endif\r
354 }\r
355 \r
356 STDMETHODIMP COutFileStream::SetSize(UInt64 newSize)\r
357 {\r
358   #ifdef USE_WIN_FILE\r
359   UInt64 currentPos;\r
360   if (!File.Seek(0, FILE_CURRENT, currentPos))\r
361     return E_FAIL;\r
362   bool result = File.SetLength(newSize);\r
363   UInt64 currentPos2;\r
364   result = result && File.Seek(currentPos, currentPos2);\r
365   return result ? S_OK : E_FAIL;\r
366   #else\r
367   return E_FAIL;\r
368   #endif\r
369 }\r
370 \r
371 #ifdef UNDER_CE\r
372 STDMETHODIMP CStdOutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize)\r
373 {\r
374   size_t s2 = fwrite(data, 1, size, stdout);\r
375   if (processedSize != 0)\r
376     *processedSize = s2;\r
377   return (s2 = size) ? S_OK : E_FAIL;\r
378 }\r
379 #else\r
380 STDMETHODIMP CStdOutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize)\r
381 {\r
382   if (processedSize != NULL)\r
383     *processedSize = 0;\r
384 \r
385   #ifdef _WIN32\r
386   UInt32 realProcessedSize;\r
387   BOOL res = TRUE;\r
388   if (size > 0)\r
389   {\r
390     // Seems that Windows doesn't like big amounts writing to stdout.\r
391     // So we limit portions by 32KB.\r
392     UInt32 sizeTemp = (1 << 15);\r
393     if (sizeTemp > size)\r
394       sizeTemp = size;\r
395     res = ::WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),\r
396         data, sizeTemp, (DWORD *)&realProcessedSize, NULL);\r
397     size -= realProcessedSize;\r
398     data = (const void *)((const Byte *)data + realProcessedSize);\r
399     if (processedSize != NULL)\r
400       *processedSize += realProcessedSize;\r
401   }\r
402   return ConvertBoolToHRESULT(res != FALSE);\r
403 \r
404   #else\r
405   \r
406   ssize_t res;\r
407   do\r
408   {\r
409     res = write(1, data, (size_t)size);\r
410   }\r
411   while (res < 0 && (errno == EINTR));\r
412   if (res == -1)\r
413     return E_FAIL;\r
414   if (processedSize != NULL)\r
415     *processedSize = (UInt32)res;\r
416   return S_OK;\r
417   \r
418   return S_OK;\r
419   #endif\r
420 }\r
421 \r
422 #endif\r