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