Logo Search packages:      
Sourcecode: tardy version File versions  Download package

gzip.cc

//
//    tardy - a tar post-processor
//    Copyright (C) 2003 Peter Miller;
//    All rights reserved.
//
//    This program is free software; you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation; either version 2 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program; if not, write to the Free Software
//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
//
// MANIFEST: functions to manipulate gzips
//

#pragma implementation "tar_output_filter_gzip"

#include <ac/stdio.h>
#include <ac/stdlib.h>
#include <ac/string.h>
#include <tar/output/filter/gzip.h>

#ifndef Z_BUFSIZE
#ifdef MAXSEG_64K
#define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */
#else
#define Z_BUFSIZE 16384
#endif
#endif



00040 tar_output_filter_gzip::~tar_output_filter_gzip()
{
    delete outbuf;
}


00046 tar_output_filter_gzip::tar_output_filter_gzip(tar_output *arg) :
    tar_output_filter(arg),
    pass_through(true),
    outbuf(0),
    crc(0)
{
}


/*
 * Outputs a long in LSB order to the given file
 *    (little endian)
 */

static void
put_long(FILE *fp, unsigned long x)
{
    unsigned char buffer[4];
    buffer[0] = x;
    buffer[1] = x >> 8;
    buffer[2] = x >> 16;
    buffer[3] = x >> 24;
    fwrite(buffer, 1, sizeof(buffer), fp);
}


void
00073 tar_output_filter_gzip::write_header(const tar_header &arg)
{
    if (arg.type != tar_header::type_normal)
    {
      pass_through = true;
      tar_output_filter::write_header(arg);
      return;
    }
    if (arg.size <= 1024)
    {
      // Don't bother with files which are too small to compress usefully.
      pass_through = true;
      tar_output_filter::write_header(arg);
      return;
    }
    pass_through = false;

    //
    // Remember the header for later use.
    //
    hdr = arg;

    //
    // Open a temporary file to hold the compressed file.
    //
    static char filename_template[] = "/tmp/tardy-XXXXXX";
    char filename[sizeof(filename_template)];
    strcpy(filename, filename_template);
    int fd = mkstemp(filename);
    if (fd < 0)
      nfatal("mkstemp \"%s\"", filename);
    temp_filename = filename;
    FILE *fp = fdopen(fd, "w+b");
    temp_fp = fp;

    //
    // Initialize the compression engine.
    //
    crc = crc32(0L, Z_NULL, 0);
    stream.avail_in = 0;
    stream.avail_out = 0;
    stream.next_in = NULL;
    stream.next_out = NULL;
    stream.opaque = (voidpf)0;
    stream.zalloc = (alloc_func)0;
    stream.zfree = (free_func)0;

    /*
     * Set the parameters for the compression.
     * Note: windowBits is passed < 0 to suppress zlib header.
     */
    int err =
      deflateInit2
      (
          &stream,
          Z_BEST_COMPRESSION, // level
          Z_DEFLATED,         // method
          -MAX_WBITS,           // windowBits
          DEF_MEM_LEVEL,      // memLevel
          Z_DEFAULT_STRATEGY  // strategy
      );
    if (err != Z_OK)
      drop_dead(err);

    if (!outbuf)
      outbuf = new Byte[Z_BUFSIZE];
    stream.next_out = outbuf;
    stream.avail_out = Z_BUFSIZE;

    /*
     * Write a very simple .gz header:
     */
    static unsigned char ghdr[] =
    {
      0x1F, 0x8B,  // gzip magic number
        Z_DEFLATED,  // method
      Z_FLAG_EXTRA_FIELD, // flags
      0, 0, 0, 0,  // time
      0,           // xflags
      3,           // always use unix OS_CODE
      4, 0,        // extra length
    };
    fwrite(ghdr, 1, sizeof(ghdr), fp);
    put_long(fp, hdr.size);
}


void
00161 tar_output_filter_gzip::write_header_padding()
{
    if (pass_through)
      tar_output_filter::write_header_padding();
}


void 
00169 tar_output_filter_gzip::write_data(const void *data, int len)
{
    if (pass_through)
    {
      tar_output_filter::write_data(data, len);
      return;
    }

    FILE *fp = (FILE *)temp_fp;
    stream.next_in = (Bytef *)data;
    stream.avail_in = len;
    while (stream.avail_in != 0)
    {
      if (stream.avail_out == 0)
      {
          fwrite(outbuf, Z_BUFSIZE, 1, fp);
          if (ferror(fp))
            nfatal("write %s", temp_filename.to_c_string());
          stream.next_out = outbuf;
          stream.avail_out = Z_BUFSIZE;
      }
      int err = deflate(&stream, Z_NO_FLUSH);
      if (err != Z_OK)
          drop_dead(err);
    }
    crc = crc32(crc, (Bytef *)data, len);
}


void
00199 tar_output_filter_gzip::write_data_padding()
{
    if (!pass_through)
    {
      FILE *fp = (FILE *)temp_fp;

      /*
       * finish sending the compressed stream
       */
      stream.avail_in = 0; /* should be zero already anyway */
      if (stream.avail_out == 0)
      {
          fwrite(outbuf, 1, Z_BUFSIZE, fp);
          if (ferror(fp))
            nfatal("write %s", temp_filename.to_c_string());
          stream.next_out = outbuf;
          stream.avail_out = Z_BUFSIZE;
      }
      for (;;)
      {
          int err = deflate(&stream, Z_FINISH);
          if (err < 0)
            drop_dead(err);
          size_t len = Z_BUFSIZE - stream.avail_out;
          if (!len)
            break;
          fwrite(outbuf, len, 1, fp);
          if (ferror(fp))
            nfatal("write %s", temp_filename.to_c_string());
          stream.next_out = outbuf;
          stream.avail_out = Z_BUFSIZE;
      }

      /*
       * and the trailer
       */
      put_long(fp, crc);
      put_long(fp, stream.total_in);
      fflush(fp);
      if (ferror(fp))
          nfatal("write %s", temp_filename.to_c_string());

      /*
       * Clean up any resources we were using.
       */
      if (stream.state != NULL)
          deflateEnd(&stream);

      //
        // Now we have the temporary file, we know the size of the
        // compressed data, so we are able to write the tar file header
        // for the compressed form into the archive.
      //
      tar_header nhdr = hdr;
      nhdr.type = tar_header::type_normal_gzipped;
      nhdr.size = ftell(fp);
      tar_output_filter::write_header(nhdr);
      tar_output_filter::write_header_padding();

      //
      // Transfer the data from the temporary file into the archive.
      //
      rewind(fp);
      for (long opos = 0; opos < nhdr.size;)
      {
          unsigned long ret;
          unsigned long nbytes = nhdr.size - opos;
          if (nbytes > Z_BUFSIZE)
            nbytes = Z_BUFSIZE;
          ret = fread(outbuf, 1, nbytes, fp);
          if (ret != nbytes)
            nfatal("read %s", temp_filename.to_c_string());
          tar_output_filter::write_data(outbuf, nbytes);
          opos += nbytes;
      }
      fclose(fp);
      temp_fp = 0;
      unlink(temp_filename.to_c_string());

      //
      // Go back to pass through mode (the default).
      //
      pass_through = true;
    }

    //
    // Write the data padding to the deeper file.
    //
    tar_output_filter::write_data_padding();
}


void
00292 tar_output_filter_gzip::drop_dead(int err)
{
    fatal("gzip: %s (%s)", z_error(err), stream.msg);
}

Generated by  Doxygen 1.6.0   Back to index