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