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