jdk/src/share/native/com/sun/java/util/jar/pack/zip.cpp
changeset 2 90ce3da70b43
child 1082 53833ff90c45
equal deleted inserted replaced
0:fd16c54261b3 2:90ce3da70b43
       
     1 /*
       
     2  * Copyright 2001-2005 Sun Microsystems, Inc.  All Rights Reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 
       
    26 /**
       
    27  * Note: Lifted from uncrunch.c from jdk sources
       
    28  */
       
    29 #include <stdio.h>
       
    30 #include <string.h>
       
    31 #include <errno.h>
       
    32 #include <time.h>
       
    33 
       
    34 #include <stdlib.h>
       
    35 
       
    36 #ifndef _MSC_VER
       
    37 #include <strings.h>
       
    38 #endif
       
    39 
       
    40 #include "defines.h"
       
    41 #include "bytes.h"
       
    42 #include "utils.h"
       
    43 
       
    44 #include "constants.h"
       
    45 #include "unpack.h"
       
    46 
       
    47 #include "zip.h"
       
    48 
       
    49 #ifdef NO_ZLIB
       
    50 
       
    51 inline bool jar::deflate_bytes(bytes& head, bytes& tail) {
       
    52   return false;
       
    53 }
       
    54 inline uint jar::get_crc32(uint c, uchar *ptr, uint len) { return 0; }
       
    55 #define Z_NULL NULL
       
    56 
       
    57 #else // Have ZLIB
       
    58 
       
    59 #include <zlib.h>
       
    60 
       
    61 inline uint jar::get_crc32(uint c, uchar *ptr, uint len) { return crc32(c, ptr, len); }
       
    62 
       
    63 #endif // End of ZLIB
       
    64 
       
    65 #ifdef sparc
       
    66 #define SWAP_BYTES(a) \
       
    67     ((((a) << 8) & 0xff00) | 0x00ff) & (((a) >> 8) | 0xff00)
       
    68 #else
       
    69 #define SWAP_BYTES(a)  (a)
       
    70 #endif
       
    71 
       
    72 #define GET_INT_LO(a) \
       
    73     SWAP_BYTES(a & 0xFFFF)
       
    74 
       
    75 #define GET_INT_HI(a) \
       
    76     SWAP_BYTES((a >> 16) & 0xFFFF);
       
    77 
       
    78 
       
    79 void jar::init(unpacker* u_) {
       
    80   BYTES_OF(*this).clear();
       
    81   u = u_;
       
    82   u->jarout = this;
       
    83 }
       
    84 
       
    85 // Write data to the ZIP output stream.
       
    86 void jar::write_data(void* buff, int len) {
       
    87   while (len > 0) {
       
    88     int rc = fwrite(buff, 1, len, jarfp);
       
    89     if (rc <= 0) {
       
    90       fprintf(u->errstrm, "Error: write on output file failed err=%d\n",errno);
       
    91       exit(1); // Called only from the native standalone unpacker
       
    92     }
       
    93     output_file_offset += rc;
       
    94     buff = ((char *)buff) + rc;
       
    95     len -= rc;
       
    96   }
       
    97 }
       
    98 
       
    99 void jar::add_to_jar_directory(const char* fname, bool store, int modtime,
       
   100                                int len, int clen, uLong crc) {
       
   101   uint fname_length = strlen(fname);
       
   102   ushort header[23];
       
   103   if (modtime == 0)  modtime = default_modtime;
       
   104   uLong dostime = get_dostime(modtime);
       
   105 
       
   106   header[0] = SWAP_BYTES(0x4B50);
       
   107   header[1] = SWAP_BYTES(0x0201);
       
   108   header[2] = SWAP_BYTES(0xA);
       
   109 
       
   110   // required version
       
   111   header[3] = SWAP_BYTES(0xA);
       
   112 
       
   113   // flags 02 = maximum  sub-compression flag
       
   114   header[4] = ( store ) ? 0x0 : SWAP_BYTES(0x2);
       
   115 
       
   116   // Compression method 8=deflate.
       
   117   header[5] = ( store ) ? 0x0 : SWAP_BYTES(0x08);
       
   118 
       
   119   // Last modified date and time.
       
   120   header[6] = GET_INT_LO(dostime);
       
   121   header[7] = GET_INT_HI(dostime);
       
   122 
       
   123   // CRC
       
   124   header[8] = GET_INT_LO(crc);
       
   125   header[9] = GET_INT_HI(crc);
       
   126 
       
   127   // Compressed length:
       
   128   header[10] = GET_INT_LO(clen);
       
   129   header[11] = GET_INT_HI(clen);
       
   130 
       
   131   // Uncompressed length.
       
   132   header[12] = GET_INT_LO(len);
       
   133   header[13] = GET_INT_HI(len);
       
   134 
       
   135   // Filename length
       
   136   header[14] = SWAP_BYTES(fname_length);
       
   137   // So called "extra field" length.
       
   138   header[15] = 0;
       
   139   // So called "comment" length.
       
   140   header[16] = 0;
       
   141   // Disk number start
       
   142   header[17] = 0;
       
   143   // File flags => binary
       
   144   header[18] = 0;
       
   145   // More file flags
       
   146   header[19] = 0;
       
   147   header[20] = 0;
       
   148   // Offset within ZIP file.
       
   149   header[21] = GET_INT_LO(output_file_offset);
       
   150   header[22] = GET_INT_HI(output_file_offset);
       
   151 
       
   152   // Copy the whole thing into the central directory.
       
   153   central_directory.append(header, sizeof(header));
       
   154 
       
   155   // Copy the fname to the header.
       
   156   central_directory.append(fname, fname_length);
       
   157 
       
   158   central_directory_count++;
       
   159 }
       
   160 
       
   161 void jar::write_jar_header(const char* fname, bool store, int modtime,
       
   162                            int len, int clen, uint crc) {
       
   163   uint fname_length = strlen(fname);
       
   164   ushort header[15];
       
   165   if (modtime == 0)  modtime = default_modtime;
       
   166   uLong dostime = get_dostime(modtime);
       
   167 
       
   168   // ZIP LOC magic.
       
   169   header[0] = SWAP_BYTES(0x4B50);
       
   170   header[1] = SWAP_BYTES(0x0403);
       
   171 
       
   172   // Version
       
   173   header[2] = SWAP_BYTES(0xA);
       
   174 
       
   175   // flags 02 = maximum  sub-compression flag
       
   176   header[3] = ( store ) ? 0x0 : SWAP_BYTES(0x2);
       
   177 
       
   178   // Compression method = deflate
       
   179   header[4] = ( store ) ? 0x0 : SWAP_BYTES(0x08);
       
   180 
       
   181   // Last modified date and time.
       
   182   header[5] = GET_INT_LO(dostime);
       
   183   header[6] = GET_INT_HI(dostime);
       
   184 
       
   185   // CRC
       
   186   header[7] = GET_INT_LO(crc);
       
   187   header[8] = GET_INT_HI(crc);
       
   188 
       
   189   // Compressed length:
       
   190   header[9] = GET_INT_LO(clen);
       
   191   header[10] = GET_INT_HI(clen);
       
   192 
       
   193   // Uncompressed length.
       
   194   header[11] = GET_INT_LO(len);
       
   195   header[12] = GET_INT_HI(len);
       
   196 
       
   197   // Filename length
       
   198   header[13] = SWAP_BYTES(fname_length);
       
   199   // So called "extra field" length.
       
   200   header[14] = 0;
       
   201 
       
   202   // Write the LOC header to the output file.
       
   203   write_data(header, sizeof(header));
       
   204 
       
   205   // Copy the fname to the header.
       
   206   write_data((char*)fname, fname_length);
       
   207 }
       
   208 
       
   209 static const char marker_comment[] = ZIP_ARCHIVE_MARKER_COMMENT;
       
   210 
       
   211 void jar::write_central_directory() {
       
   212   bytes mc; mc.set(marker_comment);
       
   213 
       
   214   ushort header[11];
       
   215 
       
   216   // Create the End of Central Directory structure.
       
   217   header[0] = SWAP_BYTES(0x4B50);
       
   218   header[1] = SWAP_BYTES(0x0605);
       
   219   // disk numbers
       
   220   header[2] = 0;
       
   221   header[3] = 0;
       
   222   // Number of entries in central directory.
       
   223   header[4] = SWAP_BYTES(central_directory_count);
       
   224   header[5] = SWAP_BYTES(central_directory_count);
       
   225   // Size of the central directory}
       
   226   header[6] = GET_INT_LO(central_directory.size());
       
   227   header[7] = GET_INT_HI(central_directory.size());
       
   228   // Offset of central directory within disk.
       
   229   header[8] = GET_INT_LO(output_file_offset);
       
   230   header[9] = GET_INT_HI(output_file_offset);
       
   231   // zipfile comment length;
       
   232   header [10] = SWAP_BYTES(mc.len);
       
   233 
       
   234   // Write the central directory.
       
   235   printcr(2, "Central directory at %d\n", output_file_offset);
       
   236   write_data(central_directory.b);
       
   237 
       
   238   // Write the End of Central Directory structure.
       
   239   printcr(2, "end-of-directory at %d\n", output_file_offset);
       
   240   write_data(header, sizeof(header));
       
   241 
       
   242   printcr(2, "writing zip comment\n");
       
   243   // Write the comment.
       
   244   write_data(mc);
       
   245 }
       
   246 
       
   247 // Public API
       
   248 
       
   249 // Open a Jar file and initialize.
       
   250 void jar::openJarFile(const char* fname) {
       
   251   if (!jarfp) {
       
   252     printcr(1, "jar::openJarFile: opening %s\n",fname);
       
   253     jarfp = fopen(fname, "wb");
       
   254     if (!jarfp) {
       
   255       fprintf(u->errstrm, "Error: Could not open jar file: %s\n",fname);
       
   256       exit(3); // Called only from the native standalone unpacker
       
   257     }
       
   258   }
       
   259 }
       
   260 
       
   261 // Add a ZIP entry and copy the file data
       
   262 void jar::addJarEntry(const char* fname,
       
   263                       bool deflate_hint, int modtime,
       
   264                       bytes& head, bytes& tail) {
       
   265   int len = head.len + tail.len;
       
   266   int clen = 0;
       
   267 
       
   268   uint crc = get_crc32(0L,Z_NULL,0);
       
   269   if (head.len != 0)
       
   270     crc = get_crc32(crc, (uchar *)head.ptr, head.len);
       
   271   if (tail.len != 0)
       
   272     crc = get_crc32(crc, (uchar *)tail.ptr, tail.len);
       
   273 
       
   274   bool deflate = (deflate_hint && len > 0);
       
   275 
       
   276   if (deflate) {
       
   277     if (deflate_bytes(head, tail) == false) {
       
   278       printcr(2, "Reverting to store fn=%s\t%d -> %d\n",
       
   279               fname, len, deflated.size());
       
   280       deflate = false;
       
   281     }
       
   282   }
       
   283   clen = (deflate) ? deflated.size() : len;
       
   284   add_to_jar_directory(fname, !deflate, modtime, len, clen, crc);
       
   285   write_jar_header(    fname, !deflate, modtime, len, clen, crc);
       
   286 
       
   287   if (deflate) {
       
   288     write_data(deflated.b);
       
   289   } else {
       
   290     write_data(head);
       
   291     write_data(tail);
       
   292   }
       
   293 }
       
   294 
       
   295 // Add a ZIP entry for a directory name no data
       
   296 void jar::addDirectoryToJarFile(const char* dir_name) {
       
   297   bool store = true;
       
   298   add_to_jar_directory((const char*)dir_name, store, default_modtime, 0, 0, 0);
       
   299   write_jar_header(    (const char*)dir_name, store, default_modtime, 0, 0, 0);
       
   300 }
       
   301 
       
   302 // Write out the central directory and close the jar file.
       
   303 void jar::closeJarFile(bool central) {
       
   304   if (jarfp) {
       
   305     fflush(jarfp);
       
   306     if (central) write_central_directory();
       
   307     fflush(jarfp);
       
   308     fclose(jarfp);
       
   309     printcr(2, "jar::closeJarFile:closed jar-file\n");
       
   310   }
       
   311   reset();
       
   312 }
       
   313 
       
   314 /* Convert the date y/n/d and time h:m:s to a four byte DOS date and
       
   315  *  time (date in high two bytes, time in low two bytes allowing magnitude
       
   316  *  comparison).
       
   317  */
       
   318 inline
       
   319 uLong jar::dostime(int y, int n, int d, int h, int m, int s) {
       
   320   return y < 1980 ? dostime(1980, 1, 1, 0, 0, 0) :
       
   321     (((uLong)y - 1980) << 25) | ((uLong)n << 21) | ((uLong)d << 16) |
       
   322     ((uLong)h << 11) | ((uLong)m << 5) | ((uLong)s >> 1);
       
   323 }
       
   324 
       
   325 #ifdef _REENTRANT // solaris
       
   326 extern "C" struct tm *gmtime_r(const time_t *, struct tm *);
       
   327 #else
       
   328 #define gmtime_r(t, s) gmtime(t)
       
   329 #endif
       
   330 /*
       
   331  * Return the Unix time in DOS format
       
   332  */
       
   333 uLong jar::get_dostime(int modtime) {
       
   334   // see defines.h
       
   335   if (modtime != 0 && modtime == modtime_cache)
       
   336     return dostime_cache;
       
   337   if (modtime != 0 && default_modtime == 0)
       
   338     default_modtime = modtime;  // catch a reasonable default
       
   339   time_t t = modtime;
       
   340   struct tm sbuf;
       
   341   struct tm* s = gmtime_r(&t, &sbuf);
       
   342   modtime_cache = modtime;
       
   343   dostime_cache = dostime(s->tm_year + 1900, s->tm_mon + 1, s->tm_mday,
       
   344                           s->tm_hour, s->tm_min, s->tm_sec);
       
   345   //printf("modtime %d => %d\n", modtime_cache, dostime_cache);
       
   346   return dostime_cache;
       
   347 }
       
   348 
       
   349 
       
   350 
       
   351 #ifndef NO_ZLIB
       
   352 
       
   353 /* Returns true on success, and will set the clen to the compressed
       
   354    length, the caller should verify if true and clen less than the
       
   355    input data
       
   356 */
       
   357 bool jar::deflate_bytes(bytes& head, bytes& tail) {
       
   358   int len = head.len + tail.len;
       
   359 
       
   360   z_stream zs;
       
   361   BYTES_OF(zs).clear();
       
   362 
       
   363   // NOTE: the window size should always be -MAX_WBITS normally -15.
       
   364   // unzip/zipup.c and java/Deflater.c
       
   365 
       
   366   int error = deflateInit2(&zs, Z_BEST_COMPRESSION, Z_DEFLATED,
       
   367                            -MAX_WBITS, 8, Z_DEFAULT_STRATEGY);
       
   368   if (error != Z_OK) {
       
   369     switch (error) {
       
   370     case Z_MEM_ERROR:
       
   371       printcr(2, "Error: deflate error : Out of memory \n");
       
   372       break;
       
   373     case Z_STREAM_ERROR:
       
   374       printcr(2,"Error: deflate error : Invalid compression level \n");
       
   375       break;
       
   376     case Z_VERSION_ERROR:
       
   377       printcr(2,"Error: deflate error : Invalid version\n");
       
   378       break;
       
   379     default:
       
   380       printcr(2,"Error: Internal deflate error error = %d\n", error);
       
   381     }
       
   382     return false;
       
   383   }
       
   384 
       
   385   deflated.empty();
       
   386   zs.next_out  = (uchar*) deflated.grow(len + (len/2));
       
   387   zs.avail_out = deflated.size();
       
   388 
       
   389   zs.next_in = (uchar*)head.ptr;
       
   390   zs.avail_in = head.len;
       
   391 
       
   392   bytes* first = &head;
       
   393   bytes* last  = &tail;
       
   394   if (last->len == 0) {
       
   395     first = null;
       
   396     last = &head;
       
   397   } else if (first->len == 0) {
       
   398     first = null;
       
   399   }
       
   400 
       
   401   if (first != null && error == Z_OK) {
       
   402     zs.next_in = (uchar*) first->ptr;
       
   403     zs.avail_in = first->len;
       
   404     error = deflate(&zs, Z_NO_FLUSH);
       
   405   }
       
   406   if (error == Z_OK) {
       
   407     zs.next_in = (uchar*) last->ptr;
       
   408     zs.avail_in = last->len;
       
   409     error = deflate(&zs, Z_FINISH);
       
   410   }
       
   411   if (error == Z_STREAM_END) {
       
   412     if (len > zs.total_out ) {
       
   413       printcr(2, "deflate compressed data %d -> %d\n", len, zs.total_out);
       
   414       deflated.b.len = zs.total_out;
       
   415       deflateEnd(&zs);
       
   416       return true;
       
   417     }
       
   418     printcr(2, "deflate expanded data %d -> %d\n", len, zs.total_out);
       
   419     deflateEnd(&zs);
       
   420     return false;
       
   421   }
       
   422 
       
   423   deflateEnd(&zs);
       
   424   printcr(2, "Error: deflate error deflate did not finish error=%d\n",error);
       
   425   return false;
       
   426 }
       
   427 
       
   428 // Callback for fetching data from a GZIP input stream
       
   429 static jlong read_input_via_gzip(unpacker* u,
       
   430                                   void* buf, jlong minlen, jlong maxlen) {
       
   431   assert(minlen <= maxlen);  // don't talk nonsense
       
   432   jlong numread = 0;
       
   433   char* bufptr = (char*) buf;
       
   434   char* inbuf = u->gzin->inbuf;
       
   435   size_t inbuflen = sizeof(u->gzin->inbuf);
       
   436   unpacker::read_input_fn_t read_gzin_fn =
       
   437     (unpacker::read_input_fn_t) u->gzin->read_input_fn;
       
   438   z_stream& zs = *(z_stream*) u->gzin->zstream;
       
   439   while (numread < minlen) {
       
   440     int readlen = (1 << 16);  // pretty arbitrary
       
   441     if (readlen > (maxlen - numread))
       
   442       readlen = (int)(maxlen - numread);
       
   443     zs.next_out = (uchar*) bufptr;
       
   444     zs.avail_out = readlen;
       
   445     if (zs.avail_in == 0) {
       
   446       zs.avail_in = (int) read_gzin_fn(u, inbuf, 1, inbuflen);
       
   447       zs.next_in = (uchar*) inbuf;
       
   448     }
       
   449     int error = inflate(&zs, Z_NO_FLUSH);
       
   450     if (error != Z_OK && error != Z_STREAM_END) {
       
   451       u->abort("error inflating input");
       
   452       break;
       
   453     }
       
   454     int nr = readlen - zs.avail_out;
       
   455     numread += nr;
       
   456     bufptr += nr;
       
   457     assert(numread <= maxlen);
       
   458     if (error == Z_STREAM_END) {
       
   459       enum { TRAILER_LEN = 8 };
       
   460       // skip 8-byte trailer
       
   461       if (zs.avail_in >= TRAILER_LEN) {
       
   462         zs.avail_in -= TRAILER_LEN;
       
   463       } else {
       
   464         // Bug: 5023768,we read past the TRAILER_LEN to see if there is
       
   465         // any extraneous data, as we dont support concatenated .gz
       
   466         // files just yet.
       
   467         int extra = (int) read_gzin_fn(u, inbuf, 1, inbuflen);
       
   468         zs.avail_in += extra - TRAILER_LEN;
       
   469       }
       
   470       // %%% should check final CRC and length here
       
   471       // %%% should check for concatenated *.gz files here
       
   472       if (zs.avail_in > 0)
       
   473         u->abort("garbage after end of deflated input stream");
       
   474       // pop this filter off:
       
   475       u->gzin->free();
       
   476       break;
       
   477     }
       
   478   }
       
   479 
       
   480   //fprintf(u->errstrm, "readInputFn(%d,%d) => %d (gunzip)\n",
       
   481   //        (int)minlen, (int)maxlen, (int)numread);
       
   482   return numread;
       
   483 }
       
   484 
       
   485 void gunzip::init(unpacker* u_) {
       
   486   BYTES_OF(*this).clear();
       
   487   u = u_;
       
   488   assert(u->gzin == null);  // once only, please
       
   489   read_input_fn = (void*)(intptr_t)u->read_input_fn;
       
   490   zstream = NEW(z_stream, 1);
       
   491   u->gzin = this;
       
   492   u->read_input_fn = read_input_via_gzip;
       
   493 }
       
   494 
       
   495 void gunzip::start(int magic) {
       
   496   assert((magic & GZIP_MAGIC_MASK) == GZIP_MAGIC);
       
   497   int gz_flg = (magic & 0xFF);  // keep "flg", discard other 3 bytes
       
   498   enum {
       
   499     FHCRC    = (1<<1),
       
   500     FEXTRA   = (1<<2),
       
   501     FNAME    = (1<<3),
       
   502     FCOMMENT = (1<<4)
       
   503   };
       
   504   char gz_mtime[4];
       
   505   char gz_xfl[1];
       
   506   char gz_os[1];
       
   507   char gz_extra_len[2];
       
   508   char gz_hcrc[2];
       
   509   char gz_ignore;
       
   510   // do not save extra, name, comment
       
   511   read_fixed_field(gz_mtime, sizeof(gz_mtime));
       
   512   read_fixed_field(gz_xfl, sizeof(gz_xfl));
       
   513   read_fixed_field(gz_os, sizeof(gz_os));
       
   514   if (gz_flg & FEXTRA) {
       
   515     read_fixed_field(gz_extra_len, sizeof(gz_extra_len));
       
   516     int extra_len = gz_extra_len[0] & 0xFF;
       
   517     extra_len += (gz_extra_len[1] & 0xFF) << 8;
       
   518     for (; extra_len > 0; extra_len--) {
       
   519       read_fixed_field(&gz_ignore, 1);
       
   520     }
       
   521   }
       
   522   int null_terms = 0;
       
   523   if (gz_flg & FNAME)     null_terms++;
       
   524   if (gz_flg & FCOMMENT)  null_terms++;
       
   525   for (; null_terms; null_terms--) {
       
   526     for (;;) {
       
   527       gz_ignore = 0;
       
   528       read_fixed_field(&gz_ignore, 1);
       
   529       if (gz_ignore == 0)  break;
       
   530     }
       
   531   }
       
   532   if (gz_flg & FHCRC)
       
   533     read_fixed_field(gz_hcrc, sizeof(gz_hcrc));
       
   534 
       
   535   if (aborting())  return;
       
   536 
       
   537   // now the input stream is ready to read into the inflater
       
   538   int error = inflateInit2((z_stream*) zstream, -MAX_WBITS);
       
   539   if (error != Z_OK) { abort("cannot create input"); return; }
       
   540 }
       
   541 
       
   542 void gunzip::free() {
       
   543   assert(u->gzin == this);
       
   544   u->gzin = null;
       
   545   u->read_input_fn = (unpacker::read_input_fn_t) this->read_input_fn;
       
   546   inflateEnd((z_stream*) zstream);
       
   547   mtrace('f', zstream, 0);
       
   548   ::free(zstream);
       
   549   zstream = null;
       
   550   mtrace('f', this, 0);
       
   551   ::free(this);
       
   552 }
       
   553 
       
   554 void gunzip::read_fixed_field(char* buf, size_t buflen) {
       
   555   if (aborting())  return;
       
   556   jlong nr = ((unpacker::read_input_fn_t)read_input_fn)
       
   557     (u, buf, buflen, buflen);
       
   558   if (nr != buflen)
       
   559     u->abort("short stream header");
       
   560 }
       
   561 
       
   562 #else // NO_ZLIB
       
   563 
       
   564 void gunzip::free() {
       
   565 }
       
   566 
       
   567 #endif // NO_ZLIB