root/branches/3.00/src/compress_gz.cc

Revision 644, 13.7 kB (checked in by fujii, 4 years ago)

Add new netinstall source.

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