]> cygwin.com Git - cygwin-apps/setup.git/blob - compress_gz.cc
* CHANGES: Update.
[cygwin-apps/setup.git] / compress_gz.cc
1 /*
2 * Copyright (c) 2001, Robert Collins.
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 Robert Collins <rbtcollins@hotmail.com>
13 *
14 */
15
16 /*
17 * Portions copyright under the zlib licence - this class was derived from
18 * gzio.c in that library.
19 */
20
21 #include "compress_gz.h"
22
23 #include <stdexcept>
24 using namespace std;
25 #include <errno.h>
26
27 #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
28 #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
29 #define ORIG_NAME 0x08 /* bit 3 set: original file name present */
30 #define COMMENT 0x10 /* bit 4 set: file comment present */
31 #define RESERVED 0xE0 /* bits 5..7: reserved */
32
33
34 /* TODO make this a static member and federate the magic logic */
35 static int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
36
37 /*
38 * Predicate: the stream is open for read. For writing the class constructor variant with
39 * mode must be called directly
40 */
41 compress_gz::compress_gz (io_stream * parent)
42 {
43 construct (parent, "r");
44 }
45
46 compress_gz::compress_gz (io_stream * parent, const char *openmode)
47 {
48 construct (parent, openmode);
49 }
50
51 void
52 compress_gz::construct (io_stream * parent, const char *openmode)
53 {
54 original = parent;
55 peeklen = 0;
56 int err;
57 int level = Z_DEFAULT_COMPRESSION; /* compression level */
58 int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */
59 char *p = (char *) openmode;
60 char fmode[80]; /* copy of openmode, without the compression level */
61 char *m = fmode;
62
63 stream.zalloc = (alloc_func) NULL;
64 stream.zfree = (free_func) NULL;
65 stream.opaque = (voidpf) NULL;
66 stream.next_in = inbuf = NULL;
67 stream.next_out = outbuf = NULL;
68 stream.avail_in = stream.avail_out = 0;
69 z_err = Z_OK;
70 z_eof = 0;
71 crc = crc32 (0L, Z_NULL, 0);
72 msg = NULL;
73 transparent = 0;
74
75 mode = '\0';
76
77 if (!parent)
78 {
79 z_err = Z_STREAM_ERROR;
80 return;
81 }
82
83 do
84 {
85 if (*p == 'r')
86 mode = 'r';
87 if (*p == 'w' || *p == 'a')
88 mode = 'w';
89 if (*p >= '0' && *p <= '9')
90 {
91 level = *p - '0';
92 }
93 else if (*p == 'f')
94 {
95 strategy = Z_FILTERED;
96 }
97 else if (*p == 'h')
98 {
99 strategy = Z_HUFFMAN_ONLY;
100 }
101 else
102 {
103 *m++ = *p; /* copy the mode */
104 }
105 }
106 while (*p++ && m != fmode + sizeof (fmode));
107 if (mode == '\0')
108 {
109 destroy ();
110 z_err = Z_STREAM_ERROR;
111 return;
112 }
113
114
115 if (mode == 'w')
116 {
117 err = deflateInit2 (&(stream), level,
118 Z_DEFLATED, -MAX_WBITS, 8, strategy);
119 /* windowBits is passed < 0 to suppress zlib header */
120
121 stream.next_out = outbuf = (Byte *) malloc (16384);
122 if (err != Z_OK || outbuf == Z_NULL)
123 {
124 destroy ();
125 z_err = Z_STREAM_ERROR;
126 return;
127 }
128 }
129 else
130 {
131
132 stream.next_in = inbuf = (unsigned char *) malloc (16384);
133 err = inflateInit2 (&stream, -MAX_WBITS);
134 /* windowBits is passed < 0 to tell that there is no zlib header.
135 * Note that in this case inflate *requires* an extra "dummy" byte
136 * after the compressed stream in order to complete decompression and
137 * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are
138 * present after the compressed stream.
139 */
140 if (err != Z_OK || inbuf == Z_NULL)
141 {
142 destroy ();
143 z_err = Z_STREAM_ERROR;
144 return;
145 }
146 }
147 stream.avail_out = 16384;
148
149 errno = 0;
150 if (mode == 'w')
151 {
152 /* Write a very simple .gz header:
153 */
154 char temp[20];
155 sprintf (temp, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1],
156 Z_DEFLATED, 0 /*flags */ , 0, 0, 0, 0 /*time */ ,
157 0 /*xflags */ , 0x0b);
158 original->write (temp, 10);
159 startpos = 10L;
160 /* We use 10L instead of ftell(s->file) to because ftell causes an
161 * fflush on some systems. This version of the library doesn't use
162 * startpos anyway in write mode, so this initialization is not
163 * necessary.
164 */
165 }
166 else
167 {
168
169 check_header (); /* skip the .gz header */
170 startpos = (original->tell () - stream.avail_in);
171 }
172
173 return;
174 }
175
176 /* ===========================================================================
177 Outputs a long in LSB order to the given file
178 */
179 void
180 compress_gz::putLong (unsigned long x)
181 {
182 int n;
183 for (n = 0; n < 4; n++)
184 {
185 unsigned char c = (unsigned char) (x & 0xff);
186 original->write (&c, 1);
187 x = x >> 8;
188 }
189 }
190
191
192 uLong
193 compress_gz::getLong ()
194 {
195 uLong x = (uLong) get_byte ();
196 int c;
197
198 x += ((uLong) get_byte ()) << 8;
199 x += ((uLong) get_byte ()) << 16;
200 c = get_byte ();
201 if (c == EOF)
202 z_err = Z_DATA_ERROR;
203 x += ((uLong) c) << 24;
204 return x;
205 }
206
207
208 ssize_t
209 compress_gz::read (void *buffer, size_t len)
210 {
211 if (!len)
212 return 0;
213
214 if (peeklen)
215 {
216 ssize_t tmplen = std::min (peeklen, len);
217 peeklen -= tmplen;
218 memcpy (buffer, peekbuf, tmplen);
219 memmove (peekbuf, peekbuf + tmplen, tmplen);
220 ssize_t tmpread = read (&((char *) buffer)[tmplen], len - tmplen);
221 if (tmpread >= 0)
222 return tmpread + tmplen;
223 else
224 return tmpread;
225 }
226
227 Bytef *start = (Bytef *) buffer; /* starting point for crc computation */
228 Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */
229
230 if (mode != 'r')
231 return Z_STREAM_ERROR;
232
233 if (z_err == Z_DATA_ERROR || z_err == Z_ERRNO)
234 return -1;
235 if (z_err == Z_STREAM_END)
236 return 0; /* EOF */
237
238 next_out = (Byte *) buffer;
239 stream.next_out = (Bytef *) buffer;
240 stream.avail_out = len;
241
242 while (stream.avail_out != 0)
243 {
244
245 if (transparent)
246 {
247 /* Copy first the lookahead bytes: */
248 uInt n = stream.avail_in;
249 if (n > stream.avail_out)
250 n = stream.avail_out;
251 if (n > 0)
252 {
253 memcpy (stream.next_out, stream.next_in, n);
254 next_out += n;
255 stream.next_out = next_out;
256 stream.next_in += n;
257 stream.avail_out -= n;
258 stream.avail_in -= n;
259 }
260 if (stream.avail_out > 0)
261 {
262 stream.avail_out -= original->read (next_out, stream.avail_out);
263 }
264 len -= stream.avail_out;
265 stream.total_in += (uLong) len;
266 stream.total_out += (uLong) len;
267 if (len == 0)
268 z_eof = 1;
269 return (int) len;
270 }
271 if (stream.avail_in == 0 && !z_eof)
272 {
273
274 errno = 0;
275 stream.avail_in = original->read (inbuf, 16384);
276 if (stream.avail_in == 0)
277 {
278 z_eof = 1;
279 if (original->error ())
280 {
281 z_err = Z_ERRNO;
282 break;
283 }
284 }
285 stream.next_in = inbuf;
286 }
287 z_err = inflate (&(stream), Z_NO_FLUSH);
288
289 if (z_err == Z_STREAM_END)
290 {
291 /* Check CRC and original size */
292 crc = crc32 (crc, start, (uInt) (stream.next_out - start));
293 start = stream.next_out;
294
295 if (getLong () != crc)
296 {
297 z_err = Z_DATA_ERROR;
298 }
299 else
300 {
301 (void) getLong ();
302 /* The uncompressed length returned by above getlong() may
303 * be different from stream.total_out) in case of
304 * concatenated .gz files. Check for such files:
305 */
306 check_header ();
307 if (z_err == Z_OK)
308 {
309 uLong total_in = stream.total_in;
310 uLong total_out = stream.total_out;
311
312 inflateReset (&(stream));
313 stream.total_in = total_in;
314 stream.total_out = total_out;
315 crc = crc32 (0L, Z_NULL, 0);
316 }
317 }
318 }
319 if (z_err != Z_OK || z_eof)
320 break;
321 }
322 crc = crc32 (crc, start, (uInt) (stream.next_out - start));
323
324 return (int) (len - stream.avail_out);
325 }
326
327
328 /* ===========================================================================
329 Writes the given number of uncompressed bytes into the compressed file.
330 gzwrite returns the number of bytes actually written (0 in case of error).
331 */
332 ssize_t
333 compress_gz::write (const void *buffer, size_t len)
334 {
335 if (mode != 'w')
336 return Z_STREAM_ERROR;
337
338 stream.next_in = (Bytef *) buffer;
339 stream.avail_in = len;
340
341 while (stream.avail_in != 0)
342 {
343
344 if (stream.avail_out == 0)
345 {
346
347 stream.next_out = outbuf;
348 if (original->write (outbuf, 16384) != 16384)
349 {
350 z_err = Z_ERRNO;
351 break;
352 }
353 stream.avail_out = 16384;
354 }
355 z_err = deflate (&(stream), Z_NO_FLUSH);
356 if (z_err != Z_OK)
357 break;
358 }
359 crc = crc32 (crc, (const Bytef *) buffer, len);
360
361 return (int) (len - stream.avail_in);
362 }
363
364 ssize_t
365 compress_gz::peek (void *buffer, size_t len)
366 {
367 if (mode != 'r')
368 return Z_STREAM_ERROR;
369 /* can only peek 512 bytes */
370 if (len > 512)
371 return ENOMEM;
372
373 if (len > peeklen)
374 {
375 size_t want = len - peeklen;
376 ssize_t got = read (&peekbuf[peeklen], want);
377 if (got >= 0)
378 peeklen += got;
379 else
380 /* error */
381 return got;
382 /* we may have read less than requested. */
383 memcpy (buffer, peekbuf, peeklen);
384 return peeklen;
385 }
386 else
387 {
388 memcpy (buffer, peekbuf, len);
389 return len;
390 }
391 }
392
393 long
394 compress_gz::tell ()
395 {
396 throw new logic_error("compress_gz::tell is not implemented");
397 }
398
399 int
400 compress_gz::seek (long where, io_stream_seek_t whence)
401 {
402 throw new logic_error("compress_gz::seek is not implemented");
403 }
404
405 int
406 compress_gz::error ()
407 {
408 if (z_err && z_err != Z_STREAM_END)
409 return z_err;
410 return 0;
411 }
412
413 int
414 compress_gz::set_mtime (int time)
415 {
416 if (original)
417 return original->set_mtime (time);
418 return 1;
419 }
420
421 int
422 compress_gz::get_mtime ()
423 {
424 if (original)
425 return original->get_mtime ();
426 return 0;
427 }
428
429 void
430 compress_gz::destroy ()
431 {
432 if (msg)
433 free (msg);
434 if (stream.state != NULL)
435 {
436 if (mode == 'w')
437 {
438 z_err = deflateEnd (&(stream));
439 }
440 else if (mode == 'r')
441 {
442 z_err = inflateEnd (&(stream));
443 }
444 }
445
446 if (inbuf)
447
448 free (inbuf);
449 if (outbuf)
450 free (outbuf);
451 if (original)
452 delete original;
453 }
454
455 compress_gz::~compress_gz ()
456 {
457 if (mode == 'w')
458 {
459 z_err = do_flush (Z_FINISH);
460 if (z_err != Z_OK)
461 {
462 destroy ();
463 return;
464 }
465
466 putLong (crc);
467 putLong (stream.total_in);
468 }
469 destroy ();
470 }
471
472 int
473 compress_gz::do_flush (int flush)
474 {
475 uInt len;
476 int done = 0;
477 if (mode != 'w')
478 return Z_STREAM_ERROR;
479 stream.avail_in = 0; /* should be zero already anyway */
480 for (;;)
481 {
482 len = 16384 - stream.avail_out;
483 if (len != 0)
484 {
485 if ((uInt) original->write (outbuf, len) != len)
486 {
487 z_err = Z_ERRNO;
488 return Z_ERRNO;
489 }
490 stream.next_out = outbuf;
491 stream.avail_out = 16384;
492 }
493 if (done)
494 break;
495 z_err = deflate (&(stream), flush);
496 /* Ignore the second of two consecutive flushes: */
497 if (len == 0 && z_err == Z_BUF_ERROR)
498 z_err = Z_OK;
499 /* deflate has finished flushing only when it hasn't used up
500 * all the available space in the output buffer:
501 */
502 done = (stream.avail_out != 0 || z_err == Z_STREAM_END);
503 if (z_err != Z_OK && z_err != Z_STREAM_END)
504 break;
505 }
506 return z_err == Z_STREAM_END ? Z_OK : z_err;
507 }
508
509
510 #if 0
511
512 gzclose (lst);
513 #endif
514 /* ===========================================================================
515 * Read a byte from a gz_stream; update next_in and avail_in. Return EOF
516 * for end of file.
517 * IN assertion: the stream s has been sucessfully opened for reading.
518 */
519 int
520 compress_gz::get_byte ()
521 {
522 if (z_eof)
523 return EOF;
524 if (stream.avail_in == 0)
525 {
526 errno = 0;
527 stream.avail_in = original->read (inbuf, 16384);
528 if (stream.avail_in == 0)
529 {
530 z_eof = 1;
531 if (original->error ())
532 z_err = Z_ERRNO;
533 return EOF;
534 }
535 stream.next_in = inbuf;
536 }
537 stream.avail_in--;
538 return *(stream.next_in)++;
539 }
540
541
542 /* ===========================================================================
543 Check the gzip header of a gz_stream opened for reading. Set the stream
544 mode to transparent if the gzip magic header is not present; set s->err
545 to Z_DATA_ERROR if the magic header is present but the rest of the header
546 is incorrect.
547 IN assertion: the stream s has already been created sucessfully;
548 s->stream.avail_in is zero for the first time, but may be non-zero
549 for concatenated .gz files.
550 */
551 void
552 compress_gz::check_header ()
553 {
554 int method; /* method byte */
555 int flags; /* flags byte */
556 uInt len;
557 int c;
558 /* Check the gzip magic header */
559 for (len = 0; len < 2; len++)
560 {
561 c = get_byte ();
562 if (c != gz_magic[len])
563 {
564 if (len != 0)
565 stream.avail_in++, stream.next_in--;
566 if (c != EOF)
567 {
568 stream.avail_in++, stream.next_in--;
569 transparent = 1;
570 }
571 z_err = stream.avail_in != 0 ? Z_OK : Z_STREAM_END;
572 return;
573 }
574 }
575 method = get_byte ();
576 flags = get_byte ();
577 if (method != Z_DEFLATED || (flags & RESERVED) != 0)
578 {
579 z_err = Z_DATA_ERROR;
580 return;
581 }
582
583 /* Discard time, xflags and OS code: */
584 for (len = 0; len < 6; len++)
585 (void) get_byte ();
586 if ((flags & EXTRA_FIELD) != 0)
587 { /* skip the extra field */
588 len = (uInt) get_byte ();
589 len += ((uInt) get_byte ()) << 8;
590 /* len is garbage if EOF but the loop below will quit anyway */
591 while (len-- != 0 && get_byte () != EOF);
592 }
593 if ((flags & ORIG_NAME) != 0)
594 { /* skip the original file name */
595 while ((c = get_byte ()) != 0 && c != EOF);
596 }
597 if ((flags & COMMENT) != 0)
598 { /* skip the .gz file comment */
599 while ((c = get_byte ()) != 0 && c != EOF);
600 }
601 if ((flags & HEAD_CRC) != 0)
602 { /* skip the header crc */
603 for (len = 0; len < 2; len++)
604 (void) get_byte ();
605 }
606 z_err = z_eof ? Z_DATA_ERROR : Z_OK;
607 }
This page took 0.061041 seconds and 5 git commands to generate.