]> cygwin.com Git - cygwin-apps/setup.git/blob - compress_lzma.cc
* nio-ftp.c (read): Read RETR status code on EOF to avoid
[cygwin-apps/setup.git] / compress_lzma.cc
1 /*
2 * Copyright (c) 2008, Charles Wilson
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * A copy of the GNU General Public License can be found at
10 * http://www.gnu.org/
11 *
12 * Written by Charles Wilson <cygwin@cygwin.com>
13 *
14 */
15
16 #include "compress_lzma.h"
17
18 #include <stdexcept>
19 using namespace std;
20 #include <errno.h>
21 #include <memory.h>
22 #include <malloc.h>
23
24 /*
25 * allocator for LzmaDec
26 */
27 static void *SzAlloc(void *p, size_t size) { p = p; return malloc(size); }
28 static void SzFree(void *p, void *address) { p = p; free(address); }
29 static ISzAlloc g_Alloc = { SzAlloc, SzFree };
30
31 /*
32 * Predicate: the stream is open for read.
33 */
34 compress_lzma::compress_lzma (io_stream * parent)
35 :
36 original(NULL),
37 owns_original(true),
38 peeklen(0),
39 lasterr(0),
40 initializedOk(true),
41 unpackSize(0),
42 thereIsSize(false),
43 inbuf(NULL),
44 outbuf(NULL),
45 outp(NULL),
46 inPos(0),
47 inSize(0),
48 outPos(0),
49 inProcessed(0),
50 outProcessed(0)
51 {
52 /* read only */
53 if (!parent || parent->error())
54 {
55 lasterr = EBADF;
56 return;
57 }
58 original = parent;
59
60 inbuf = (unsigned char *) malloc (IN_BUF_SIZE);
61 if (!inbuf)
62 {
63 lasterr = ENOMEM;
64 return;
65 }
66
67 outbuf = (unsigned char *) malloc (OUT_BUF_SIZE);
68 if (!outbuf)
69 {
70 lasterr = ENOMEM;
71 destroy();
72 return;
73 }
74
75 errno = 0;
76 check_header (); /* parse the header, and position fp */
77
78 outPos = 0;
79 inPos = 0;
80 inSize = 0;
81 outp = outbuf;
82 inProcessed = 0;
83 outProcessed = 0;
84 return;
85 }
86
87 ssize_t
88 compress_lzma::read (void *buffer, size_t len)
89 {
90
91 if (!this->initializedOk)
92 return -1;
93
94 /* there is no recovery from a busted stream */
95 if (this->lasterr)
96 return -1;
97
98 if (len == 0)
99 return 0;
100
101 /* peekbuf is layered on top of existing buffering code */
102 if (this->peeklen)
103 {
104 ssize_t tmplen = std::min (this->peeklen, len);
105 this->peeklen -= tmplen;
106 memcpy (buffer, this->peekbuf, tmplen);
107 memmove (this->peekbuf, this->peekbuf + tmplen, sizeof(this->peekbuf) - tmplen);
108 ssize_t tmpread = read (&((char *) buffer)[tmplen], len - tmplen);
109 if (tmpread >= 0)
110 return tmpread + tmplen;
111 else
112 return tmpread;
113 }
114
115 if (this->outp < this->outbuf + this->outPos)
116 /* outp - outbuf < outPos, but avoid sign/unsigned warning */
117 {
118 ssize_t tmplen = std::min ((size_t)(this->outbuf + this->outPos - this->outp), len);
119 memcpy (buffer, this->outp, tmplen);
120 this->outp += tmplen;
121 ssize_t tmpread = read (&((char *) buffer)[tmplen], len - tmplen);
122 if (tmpread >= 0)
123 return tmpread + tmplen;
124 else
125 return tmpread;
126 }
127
128 size_t lenRemaining = len;
129 unsigned char * bufp = (unsigned char *)buffer;
130 /* if we made it here, any existing uncompressed data in outbuf
131 * has been consumed, so reset outp and outPos
132 */
133 this->outp = this->outbuf;
134 this->outPos = 0;
135 do
136 {
137 if (this->inPos == this->inSize)
138 {
139 this->inSize = (size_t) this->original->read(this->inbuf, IN_BUF_SIZE);
140 this->inPos = 0;
141 }
142
143 /* at this point, these two variables actually hold the number
144 * of bytes *available* to be processed or filled
145 */
146 this->inProcessed = this->inSize - this->inPos;
147 this->outProcessed = OUT_BUF_SIZE - this->outPos;
148
149
150 ELzmaFinishMode finishMode = LZMA_FINISH_ANY;
151 if (this->thereIsSize && this->outProcessed > this->unpackSize)
152 {
153 this->outProcessed = unpackSize;
154 finishMode = LZMA_FINISH_END;
155 }
156
157 ELzmaStatus status;
158 int res = LzmaDec_DecodeToBuf(
159 &(this->state),
160 this->outbuf + this->outPos,
161 &(this->outProcessed),
162 this->inbuf + this->inPos,
163 &(this->inProcessed),
164 finishMode,
165 &status);
166 this->inPos += (UInt32)this->inProcessed;
167 this->outPos += this->outProcessed;
168 this->unpackSize -= this->outProcessed;
169
170 ssize_t tmplen = std::min (this->outProcessed, lenRemaining);
171 memcpy (bufp, outp, tmplen);
172 outp += tmplen;
173 bufp += tmplen;
174 lenRemaining -= tmplen;
175
176 if (res != SZ_OK)
177 {
178 lasterr = res;
179 return -1;
180 }
181
182
183 if (this->thereIsSize && this->unpackSize == 0)
184 {
185 /* expected EOF */
186 break;
187 }
188 if (this->inProcessed == 0 && this->outProcessed == 0)
189 {
190 if (this->thereIsSize || status != LZMA_STATUS_FINISHED_WITH_MARK)
191 {
192 lasterr = EIO;
193 return -1;
194 }
195 break;
196 }
197 if (lenRemaining == 0)
198 {
199 /* fulfilled request */
200 break;
201 }
202 }
203 while (true);
204
205 return (len - lenRemaining);
206 }
207
208 ssize_t
209 compress_lzma::write (const void *buffer, size_t len)
210 {
211 throw new logic_error("compress_lzma::write is not implemented");
212 }
213
214 ssize_t
215 compress_lzma::peek (void *buffer, size_t len)
216 {
217 /* can only peek 512 bytes */
218 if (len > 512)
219 return ENOMEM;
220
221 if (len > this->peeklen)
222 {
223 size_t want = len - this->peeklen;
224 ssize_t got = read (&(this->peekbuf[peeklen]), want);
225 if (got >= 0)
226 this->peeklen += got;
227 else
228 /* error */
229 return got;
230 /* we may have read less than requested. */
231 memcpy (buffer, this->peekbuf, this->peeklen);
232 return this->peeklen;
233 }
234 else
235 {
236 memcpy (buffer, this->peekbuf, len);
237 return len;
238 }
239 return 0;
240 }
241
242 long
243 compress_lzma::tell ()
244 {
245 throw new logic_error("compress_lzma::tell is not implemented");
246 }
247
248 int
249 compress_lzma::seek (long where, io_stream_seek_t whence)
250 {
251 throw new logic_error("compress_lzma::seek is not implemented");
252 }
253
254 int
255 compress_lzma::error ()
256 {
257 return lasterr;
258 }
259
260 int
261 compress_lzma::set_mtime (time_t mtime)
262 {
263 if (original)
264 return original->set_mtime (mtime);
265 return 1;
266 }
267
268 time_t
269 compress_lzma::get_mtime ()
270 {
271 if (original)
272 return original->get_mtime ();
273 return 0;
274 }
275
276 mode_t
277 compress_lzma::get_mode ()
278 {
279 if (original)
280 return original->get_mode ();
281 return 0;
282 }
283
284 void
285 compress_lzma::release_original ()
286 {
287 owns_original = false;
288 }
289
290 void
291 compress_lzma::destroy ()
292 {
293 if (this->initializedOk)
294 {
295 LzmaDec_Free(&(this->state), &g_Alloc);
296 }
297 if (this->inbuf)
298 {
299 free (this->inbuf);
300 this->inbuf = NULL;
301 }
302 if (this->outbuf)
303 {
304 free (this->outbuf);
305 this->outbuf = NULL;
306 }
307
308 if (original && owns_original)
309 delete original;
310 }
311
312 compress_lzma::~compress_lzma ()
313 {
314 destroy ();
315 }
316
317 /* ===========================================================================
318 * Check the header of a lzma_stream opened for reading.
319 * IN assertion:
320 * the stream s has already been created sucessfully
321 * this method is called only once per stream
322 */
323 void
324 compress_lzma::check_header ()
325 {
326 this->initializedOk = false;
327 this->thereIsSize = false;
328 this->unpackSize = 0;
329
330 /* read properties */
331 if (this->original->read(this->header, sizeof(this->header)) != sizeof(this->header))
332 {
333 this->lasterr = (errno ? errno : EIO);
334 return;
335 }
336
337 /* read uncompressed size */
338 for (int i = 0; i < 8; i++)
339 {
340 unsigned char b = this->header[LZMA_PROPS_SIZE + i];
341 if (b != 0xFF)
342 {
343 this->thereIsSize = true;
344 }
345 this->unpackSize += (UInt64)b << (i * 8);
346 }
347
348 /* Decode LZMA properties and allocate memory */
349 LzmaDec_Construct(&(this->state));
350 int res = LzmaDec_Allocate(&(this->state), this->header, LZMA_PROPS_SIZE, &g_Alloc);
351 if (res != SZ_OK)
352 {
353 lasterr = res;
354 return;
355 }
356
357 LzmaDec_Init(&(this->state));
358 initializedOk = true;
359 }
360
361 /* ===========================================================================
362 * duplicates a lot of code in check_header, but we don't want
363 * to read "too much" from the stream in the check_header case.
364 * Here, we don't care because we know compress will call this
365 * method with a peek'ed buffer. We also do not want to modify
366 * the member variables 'header' and 'state'.
367 */
368 bool
369 compress_lzma::is_lzma (void * buffer, size_t len)
370 {
371 unsigned char local_header[LZMA_PROPS_SIZE + 8];
372 CLzmaDec local_state;
373 UInt64 local_unpackSize = 0;
374 bool local_thereIsSize = false;
375
376 /* read header */
377 if (len < LZMA_PROPS_SIZE + 8)
378 {
379 return false;
380 }
381 memcpy((void*)local_header, buffer, sizeof(local_header));
382
383 /* read uncompressed size */
384 for (int i = 0; i < 8; i++)
385 {
386 unsigned char b = local_header[LZMA_PROPS_SIZE + i];
387 if (b != 0xFF)
388 {
389 local_thereIsSize = true;
390 }
391 local_unpackSize += (UInt64)b << (i * 8);
392 }
393
394 /* decode header */
395 if (LzmaProps_Decode(&(local_state.prop), local_header, LZMA_PROPS_SIZE) != SZ_OK)
396 {
397 return false;
398 }
399
400 return true;
401 }
402
This page took 0.053296 seconds and 5 git commands to generate.