kusano fc6ab3
/* gzappend -- command to append to a gzip file
kusano fc6ab3
kusano fc6ab3
  Copyright (C) 2003, 2012 Mark Adler, all rights reserved
kusano fc6ab3
  version 1.2, 11 Oct 2012
kusano fc6ab3
kusano fc6ab3
  This software is provided 'as-is', without any express or implied
kusano fc6ab3
  warranty.  In no event will the author be held liable for any damages
kusano fc6ab3
  arising from the use of this software.
kusano fc6ab3
kusano fc6ab3
  Permission is granted to anyone to use this software for any purpose,
kusano fc6ab3
  including commercial applications, and to alter it and redistribute it
kusano fc6ab3
  freely, subject to the following restrictions:
kusano fc6ab3
kusano fc6ab3
  1. The origin of this software must not be misrepresented; you must not
kusano fc6ab3
     claim that you wrote the original software. If you use this software
kusano fc6ab3
     in a product, an acknowledgment in the product documentation would be
kusano fc6ab3
     appreciated but is not required.
kusano fc6ab3
  2. Altered source versions must be plainly marked as such, and must not be
kusano fc6ab3
     misrepresented as being the original software.
kusano fc6ab3
  3. This notice may not be removed or altered from any source distribution.
kusano fc6ab3
kusano fc6ab3
  Mark Adler    madler@alumni.caltech.edu
kusano fc6ab3
 */
kusano fc6ab3
kusano fc6ab3
/*
kusano fc6ab3
 * Change history:
kusano fc6ab3
 *
kusano fc6ab3
 * 1.0  19 Oct 2003     - First version
kusano fc6ab3
 * 1.1   4 Nov 2003     - Expand and clarify some comments and notes
kusano fc6ab3
 *                      - Add version and copyright to help
kusano fc6ab3
 *                      - Send help to stdout instead of stderr
kusano fc6ab3
 *                      - Add some preemptive typecasts
kusano fc6ab3
 *                      - Add L to constants in lseek() calls
kusano fc6ab3
 *                      - Remove some debugging information in error messages
kusano fc6ab3
 *                      - Use new data_type definition for zlib 1.2.1
kusano fc6ab3
 *                      - Simplfy and unify file operations
kusano fc6ab3
 *                      - Finish off gzip file in gztack()
kusano fc6ab3
 *                      - Use deflatePrime() instead of adding empty blocks
kusano fc6ab3
 *                      - Keep gzip file clean on appended file read errors
kusano fc6ab3
 *                      - Use in-place rotate instead of auxiliary buffer
kusano fc6ab3
 *                        (Why you ask?  Because it was fun to write!)
kusano fc6ab3
 * 1.2  11 Oct 2012     - Fix for proper z_const usage
kusano fc6ab3
 *                      - Check for input buffer malloc failure
kusano fc6ab3
 */
kusano fc6ab3
kusano fc6ab3
/*
kusano fc6ab3
   gzappend takes a gzip file and appends to it, compressing files from the
kusano fc6ab3
   command line or data from stdin.  The gzip file is written to directly, to
kusano fc6ab3
   avoid copying that file, in case it's large.  Note that this results in the
kusano fc6ab3
   unfriendly behavior that if gzappend fails, the gzip file is corrupted.
kusano fc6ab3
kusano fc6ab3
   This program was written to illustrate the use of the new Z_BLOCK option of
kusano fc6ab3
   zlib 1.2.x's inflate() function.  This option returns from inflate() at each
kusano fc6ab3
   block boundary to facilitate locating and modifying the last block bit at
kusano fc6ab3
   the start of the final deflate block.  Also whether using Z_BLOCK or not,
kusano fc6ab3
   another required feature of zlib 1.2.x is that inflate() now provides the
kusano fc6ab3
   number of unusued bits in the last input byte used.  gzappend will not work
kusano fc6ab3
   with versions of zlib earlier than 1.2.1.
kusano fc6ab3
kusano fc6ab3
   gzappend first decompresses the gzip file internally, discarding all but
kusano fc6ab3
   the last 32K of uncompressed data, and noting the location of the last block
kusano fc6ab3
   bit and the number of unused bits in the last byte of the compressed data.
kusano fc6ab3
   The gzip trailer containing the CRC-32 and length of the uncompressed data
kusano fc6ab3
   is verified.  This trailer will be later overwritten.
kusano fc6ab3
kusano fc6ab3
   Then the last block bit is cleared by seeking back in the file and rewriting
kusano fc6ab3
   the byte that contains it.  Seeking forward, the last byte of the compressed
kusano fc6ab3
   data is saved along with the number of unused bits to initialize deflate.
kusano fc6ab3
kusano fc6ab3
   A deflate process is initialized, using the last 32K of the uncompressed
kusano fc6ab3
   data from the gzip file to initialize the dictionary.  If the total
kusano fc6ab3
   uncompressed data was less than 32K, then all of it is used to initialize
kusano fc6ab3
   the dictionary.  The deflate output bit buffer is also initialized with the
kusano fc6ab3
   last bits from the original deflate stream.  From here on, the data to
kusano fc6ab3
   append is simply compressed using deflate, and written to the gzip file.
kusano fc6ab3
   When that is complete, the new CRC-32 and uncompressed length are written
kusano fc6ab3
   as the trailer of the gzip file.
kusano fc6ab3
 */
kusano fc6ab3
kusano fc6ab3
#include <stdio.h></stdio.h>
kusano fc6ab3
#include <stdlib.h></stdlib.h>
kusano fc6ab3
#include <string.h></string.h>
kusano fc6ab3
#include <fcntl.h></fcntl.h>
kusano fc6ab3
#include <unistd.h></unistd.h>
kusano fc6ab3
#include "zlib.h"
kusano fc6ab3
kusano fc6ab3
#define local static
kusano fc6ab3
#define LGCHUNK 14
kusano fc6ab3
#define CHUNK (1U << LGCHUNK)
kusano fc6ab3
#define DSIZE 32768U
kusano fc6ab3
kusano fc6ab3
/* print an error message and terminate with extreme prejudice */
kusano fc6ab3
local void bye(char *msg1, char *msg2)
kusano fc6ab3
{
kusano fc6ab3
    fprintf(stderr, "gzappend error: %s%s\n", msg1, msg2);
kusano fc6ab3
    exit(1);
kusano fc6ab3
}
kusano fc6ab3
kusano fc6ab3
/* return the greatest common divisor of a and b using Euclid's algorithm,
kusano fc6ab3
   modified to be fast when one argument much greater than the other, and
kusano fc6ab3
   coded to avoid unnecessary swapping */
kusano fc6ab3
local unsigned gcd(unsigned a, unsigned b)
kusano fc6ab3
{
kusano fc6ab3
    unsigned c;
kusano fc6ab3
kusano fc6ab3
    while (a && b)
kusano fc6ab3
        if (a > b) {
kusano fc6ab3
            c = b;
kusano fc6ab3
            while (a - c >= c)
kusano fc6ab3
                c <<= 1;
kusano fc6ab3
            a -= c;
kusano fc6ab3
        }
kusano fc6ab3
        else {
kusano fc6ab3
            c = a;
kusano fc6ab3
            while (b - c >= c)
kusano fc6ab3
                c <<= 1;
kusano fc6ab3
            b -= c;
kusano fc6ab3
        }
kusano fc6ab3
    return a + b;
kusano fc6ab3
}
kusano fc6ab3
kusano fc6ab3
/* rotate list[0..len-1] left by rot positions, in place */
kusano fc6ab3
local void rotate(unsigned char *list, unsigned len, unsigned rot)
kusano fc6ab3
{
kusano fc6ab3
    unsigned char tmp;
kusano fc6ab3
    unsigned cycles;
kusano fc6ab3
    unsigned char *start, *last, *to, *from;
kusano fc6ab3
kusano fc6ab3
    /* normalize rot and handle degenerate cases */
kusano fc6ab3
    if (len < 2) return;
kusano fc6ab3
    if (rot >= len) rot %= len;
kusano fc6ab3
    if (rot == 0) return;
kusano fc6ab3
kusano fc6ab3
    /* pointer to last entry in list */
kusano fc6ab3
    last = list + (len - 1);
kusano fc6ab3
kusano fc6ab3
    /* do simple left shift by one */
kusano fc6ab3
    if (rot == 1) {
kusano fc6ab3
        tmp = *list;
kusano fc6ab3
        memcpy(list, list + 1, len - 1);
kusano fc6ab3
        *last = tmp;
kusano fc6ab3
        return;
kusano fc6ab3
    }
kusano fc6ab3
kusano fc6ab3
    /* do simple right shift by one */
kusano fc6ab3
    if (rot == len - 1) {
kusano fc6ab3
        tmp = *last;
kusano fc6ab3
        memmove(list + 1, list, len - 1);
kusano fc6ab3
        *list = tmp;
kusano fc6ab3
        return;
kusano fc6ab3
    }
kusano fc6ab3
kusano fc6ab3
    /* otherwise do rotate as a set of cycles in place */
kusano fc6ab3
    cycles = gcd(len, rot);             /* number of cycles */
kusano fc6ab3
    do {
kusano fc6ab3
        start = from = list + cycles;   /* start index is arbitrary */
kusano fc6ab3
        tmp = *from;                    /* save entry to be overwritten */
kusano fc6ab3
        for (;;) {
kusano fc6ab3
            to = from;                  /* next step in cycle */
kusano fc6ab3
            from += rot;                /* go right rot positions */
kusano fc6ab3
            if (from > last) from -= len;   /* (pointer better not wrap) */
kusano fc6ab3
            if (from == start) break;   /* all but one shifted */
kusano fc6ab3
            *to = *from;                /* shift left */
kusano fc6ab3
        }
kusano fc6ab3
        *to = tmp;                      /* complete the circle */
kusano fc6ab3
    } while (--cycles);
kusano fc6ab3
}
kusano fc6ab3
kusano fc6ab3
/* structure for gzip file read operations */
kusano fc6ab3
typedef struct {
kusano fc6ab3
    int fd;                     /* file descriptor */
kusano fc6ab3
    int size;                   /* 1 << size is bytes in buf */
kusano fc6ab3
    unsigned left;              /* bytes available at next */
kusano fc6ab3
    unsigned char *buf;         /* buffer */
kusano fc6ab3
    z_const unsigned char *next;    /* next byte in buffer */
kusano fc6ab3
    char *name;                 /* file name for error messages */
kusano fc6ab3
} file;
kusano fc6ab3
kusano fc6ab3
/* reload buffer */
kusano fc6ab3
local int readin(file *in)
kusano fc6ab3
{
kusano fc6ab3
    int len;
kusano fc6ab3
kusano fc6ab3
    len = read(in->fd, in->buf, 1 << in->size);
kusano fc6ab3
    if (len == -1) bye("error reading ", in->name);
kusano fc6ab3
    in->left = (unsigned)len;
kusano fc6ab3
    in->next = in->buf;
kusano fc6ab3
    return len;
kusano fc6ab3
}
kusano fc6ab3
kusano fc6ab3
/* read from file in, exit if end-of-file */
kusano fc6ab3
local int readmore(file *in)
kusano fc6ab3
{
kusano fc6ab3
    if (readin(in) == 0) bye("unexpected end of ", in->name);
kusano fc6ab3
    return 0;
kusano fc6ab3
}
kusano fc6ab3
kusano fc6ab3
#define read1(in) (in->left == 0 ? readmore(in) : 0, \
kusano fc6ab3
                   in->left--, *(in->next)++)
kusano fc6ab3
kusano fc6ab3
/* skip over n bytes of in */
kusano fc6ab3
local void skip(file *in, unsigned n)
kusano fc6ab3
{
kusano fc6ab3
    unsigned bypass;
kusano fc6ab3
kusano fc6ab3
    if (n > in->left) {
kusano fc6ab3
        n -= in->left;
kusano fc6ab3
        bypass = n & ~((1U << in->size) - 1);
kusano fc6ab3
        if (bypass) {
kusano fc6ab3
            if (lseek(in->fd, (off_t)bypass, SEEK_CUR) == -1)
kusano fc6ab3
                bye("seeking ", in->name);
kusano fc6ab3
            n -= bypass;
kusano fc6ab3
        }
kusano fc6ab3
        readmore(in);
kusano fc6ab3
        if (n > in->left)
kusano fc6ab3
            bye("unexpected end of ", in->name);
kusano fc6ab3
    }
kusano fc6ab3
    in->left -= n;
kusano fc6ab3
    in->next += n;
kusano fc6ab3
}
kusano fc6ab3
kusano fc6ab3
/* read a four-byte unsigned integer, little-endian, from in */
kusano fc6ab3
unsigned long read4(file *in)
kusano fc6ab3
{
kusano fc6ab3
    unsigned long val;
kusano fc6ab3
kusano fc6ab3
    val = read1(in);
kusano fc6ab3
    val += (unsigned)read1(in) << 8;
kusano fc6ab3
    val += (unsigned long)read1(in) << 16;
kusano fc6ab3
    val += (unsigned long)read1(in) << 24;
kusano fc6ab3
    return val;
kusano fc6ab3
}
kusano fc6ab3
kusano fc6ab3
/* skip over gzip header */
kusano fc6ab3
local void gzheader(file *in)
kusano fc6ab3
{
kusano fc6ab3
    int flags;
kusano fc6ab3
    unsigned n;
kusano fc6ab3
kusano fc6ab3
    if (read1(in) != 31 || read1(in) != 139) bye(in->name, " not a gzip file");
kusano fc6ab3
    if (read1(in) != 8) bye("unknown compression method in", in->name);
kusano fc6ab3
    flags = read1(in);
kusano fc6ab3
    if (flags & 0xe0) bye("unknown header flags set in", in->name);
kusano fc6ab3
    skip(in, 6);
kusano fc6ab3
    if (flags & 4) {
kusano fc6ab3
        n = read1(in);
kusano fc6ab3
        n += (unsigned)(read1(in)) << 8;
kusano fc6ab3
        skip(in, n);
kusano fc6ab3
    }
kusano fc6ab3
    if (flags & 8) while (read1(in) != 0) ;
kusano fc6ab3
    if (flags & 16) while (read1(in) != 0) ;
kusano fc6ab3
    if (flags & 2) skip(in, 2);
kusano fc6ab3
}
kusano fc6ab3
kusano fc6ab3
/* decompress gzip file "name", return strm with a deflate stream ready to
kusano fc6ab3
   continue compression of the data in the gzip file, and return a file
kusano fc6ab3
   descriptor pointing to where to write the compressed data -- the deflate
kusano fc6ab3
   stream is initialized to compress using level "level" */
kusano fc6ab3
local int gzscan(char *name, z_stream *strm, int level)
kusano fc6ab3
{
kusano fc6ab3
    int ret, lastbit, left, full;
kusano fc6ab3
    unsigned have;
kusano fc6ab3
    unsigned long crc, tot;
kusano fc6ab3
    unsigned char *window;
kusano fc6ab3
    off_t lastoff, end;
kusano fc6ab3
    file gz;
kusano fc6ab3
kusano fc6ab3
    /* open gzip file */
kusano fc6ab3
    gz.name = name;
kusano fc6ab3
    gz.fd = open(name, O_RDWR, 0);
kusano fc6ab3
    if (gz.fd == -1) bye("cannot open ", name);
kusano fc6ab3
    gz.buf = malloc(CHUNK);
kusano fc6ab3
    if (gz.buf == NULL) bye("out of memory", "");
kusano fc6ab3
    gz.size = LGCHUNK;
kusano fc6ab3
    gz.left = 0;
kusano fc6ab3
kusano fc6ab3
    /* skip gzip header */
kusano fc6ab3
    gzheader(&gz);
kusano fc6ab3
kusano fc6ab3
    /* prepare to decompress */
kusano fc6ab3
    window = malloc(DSIZE);
kusano fc6ab3
    if (window == NULL) bye("out of memory", "");
kusano fc6ab3
    strm->zalloc = Z_NULL;
kusano fc6ab3
    strm->zfree = Z_NULL;
kusano fc6ab3
    strm->opaque = Z_NULL;
kusano fc6ab3
    ret = inflateInit2(strm, -15);
kusano fc6ab3
    if (ret != Z_OK) bye("out of memory", " or library mismatch");
kusano fc6ab3
kusano fc6ab3
    /* decompress the deflate stream, saving append information */
kusano fc6ab3
    lastbit = 0;
kusano fc6ab3
    lastoff = lseek(gz.fd, 0L, SEEK_CUR) - gz.left;
kusano fc6ab3
    left = 0;
kusano fc6ab3
    strm->avail_in = gz.left;
kusano fc6ab3
    strm->next_in = gz.next;
kusano fc6ab3
    crc = crc32(0L, Z_NULL, 0);
kusano fc6ab3
    have = full = 0;
kusano fc6ab3
    do {
kusano fc6ab3
        /* if needed, get more input */
kusano fc6ab3
        if (strm->avail_in == 0) {
kusano fc6ab3
            readmore(&gz);
kusano fc6ab3
            strm->avail_in = gz.left;
kusano fc6ab3
            strm->next_in = gz.next;
kusano fc6ab3
        }
kusano fc6ab3
kusano fc6ab3
        /* set up output to next available section of sliding window */
kusano fc6ab3
        strm->avail_out = DSIZE - have;
kusano fc6ab3
        strm->next_out = window + have;
kusano fc6ab3
kusano fc6ab3
        /* inflate and check for errors */
kusano fc6ab3
        ret = inflate(strm, Z_BLOCK);
kusano fc6ab3
        if (ret == Z_STREAM_ERROR) bye("internal stream error!", "");
kusano fc6ab3
        if (ret == Z_MEM_ERROR) bye("out of memory", "");
kusano fc6ab3
        if (ret == Z_DATA_ERROR)
kusano fc6ab3
            bye("invalid compressed data--format violated in", name);
kusano fc6ab3
kusano fc6ab3
        /* update crc and sliding window pointer */
kusano fc6ab3
        crc = crc32(crc, window + have, DSIZE - have - strm->avail_out);
kusano fc6ab3
        if (strm->avail_out)
kusano fc6ab3
            have = DSIZE - strm->avail_out;
kusano fc6ab3
        else {
kusano fc6ab3
            have = 0;
kusano fc6ab3
            full = 1;
kusano fc6ab3
        }
kusano fc6ab3
kusano fc6ab3
        /* process end of block */
kusano fc6ab3
        if (strm->data_type & 128) {
kusano fc6ab3
            if (strm->data_type & 64)
kusano fc6ab3
                left = strm->data_type & 0x1f;
kusano fc6ab3
            else {
kusano fc6ab3
                lastbit = strm->data_type & 0x1f;
kusano fc6ab3
                lastoff = lseek(gz.fd, 0L, SEEK_CUR) - strm->avail_in;
kusano fc6ab3
            }
kusano fc6ab3
        }
kusano fc6ab3
    } while (ret != Z_STREAM_END);
kusano fc6ab3
    inflateEnd(strm);
kusano fc6ab3
    gz.left = strm->avail_in;
kusano fc6ab3
    gz.next = strm->next_in;
kusano fc6ab3
kusano fc6ab3
    /* save the location of the end of the compressed data */
kusano fc6ab3
    end = lseek(gz.fd, 0L, SEEK_CUR) - gz.left;
kusano fc6ab3
kusano fc6ab3
    /* check gzip trailer and save total for deflate */
kusano fc6ab3
    if (crc != read4(&gz))
kusano fc6ab3
        bye("invalid compressed data--crc mismatch in ", name);
kusano fc6ab3
    tot = strm->total_out;
kusano fc6ab3
    if ((tot & 0xffffffffUL) != read4(&gz))
kusano fc6ab3
        bye("invalid compressed data--length mismatch in", name);
kusano fc6ab3
kusano fc6ab3
    /* if not at end of file, warn */
kusano fc6ab3
    if (gz.left || readin(&gz))
kusano fc6ab3
        fprintf(stderr,
kusano fc6ab3
            "gzappend warning: junk at end of gzip file overwritten\n");
kusano fc6ab3
kusano fc6ab3
    /* clear last block bit */
kusano fc6ab3
    lseek(gz.fd, lastoff - (lastbit != 0), SEEK_SET);
kusano fc6ab3
    if (read(gz.fd, gz.buf, 1) != 1) bye("reading after seek on ", name);
kusano fc6ab3
    *gz.buf = (unsigned char)(*gz.buf ^ (1 << ((8 - lastbit) & 7)));
kusano fc6ab3
    lseek(gz.fd, -1L, SEEK_CUR);
kusano fc6ab3
    if (write(gz.fd, gz.buf, 1) != 1) bye("writing after seek to ", name);
kusano fc6ab3
kusano fc6ab3
    /* if window wrapped, build dictionary from window by rotating */
kusano fc6ab3
    if (full) {
kusano fc6ab3
        rotate(window, DSIZE, have);
kusano fc6ab3
        have = DSIZE;
kusano fc6ab3
    }
kusano fc6ab3
kusano fc6ab3
    /* set up deflate stream with window, crc, total_in, and leftover bits */
kusano fc6ab3
    ret = deflateInit2(strm, level, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
kusano fc6ab3
    if (ret != Z_OK) bye("out of memory", "");
kusano fc6ab3
    deflateSetDictionary(strm, window, have);
kusano fc6ab3
    strm->adler = crc;
kusano fc6ab3
    strm->total_in = tot;
kusano fc6ab3
    if (left) {
kusano fc6ab3
        lseek(gz.fd, --end, SEEK_SET);
kusano fc6ab3
        if (read(gz.fd, gz.buf, 1) != 1) bye("reading after seek on ", name);
kusano fc6ab3
        deflatePrime(strm, 8 - left, *gz.buf);
kusano fc6ab3
    }
kusano fc6ab3
    lseek(gz.fd, end, SEEK_SET);
kusano fc6ab3
kusano fc6ab3
    /* clean up and return */
kusano fc6ab3
    free(window);
kusano fc6ab3
    free(gz.buf);
kusano fc6ab3
    return gz.fd;
kusano fc6ab3
}
kusano fc6ab3
kusano fc6ab3
/* append file "name" to gzip file gd using deflate stream strm -- if last
kusano fc6ab3
   is true, then finish off the deflate stream at the end */
kusano fc6ab3
local void gztack(char *name, int gd, z_stream *strm, int last)
kusano fc6ab3
{
kusano fc6ab3
    int fd, len, ret;
kusano fc6ab3
    unsigned left;
kusano fc6ab3
    unsigned char *in, *out;
kusano fc6ab3
kusano fc6ab3
    /* open file to compress and append */
kusano fc6ab3
    fd = 0;
kusano fc6ab3
    if (name != NULL) {
kusano fc6ab3
        fd = open(name, O_RDONLY, 0);
kusano fc6ab3
        if (fd == -1)
kusano fc6ab3
            fprintf(stderr, "gzappend warning: %s not found, skipping ...\n",
kusano fc6ab3
                    name);
kusano fc6ab3
    }
kusano fc6ab3
kusano fc6ab3
    /* allocate buffers */
kusano fc6ab3
    in = malloc(CHUNK);
kusano fc6ab3
    out = malloc(CHUNK);
kusano fc6ab3
    if (in == NULL || out == NULL) bye("out of memory", "");
kusano fc6ab3
kusano fc6ab3
    /* compress input file and append to gzip file */
kusano fc6ab3
    do {
kusano fc6ab3
        /* get more input */
kusano fc6ab3
        len = read(fd, in, CHUNK);
kusano fc6ab3
        if (len == -1) {
kusano fc6ab3
            fprintf(stderr,
kusano fc6ab3
                    "gzappend warning: error reading %s, skipping rest ...\n",
kusano fc6ab3
                    name);
kusano fc6ab3
            len = 0;
kusano fc6ab3
        }
kusano fc6ab3
        strm->avail_in = (unsigned)len;
kusano fc6ab3
        strm->next_in = in;
kusano fc6ab3
        if (len) strm->adler = crc32(strm->adler, in, (unsigned)len);
kusano fc6ab3
kusano fc6ab3
        /* compress and write all available output */
kusano fc6ab3
        do {
kusano fc6ab3
            strm->avail_out = CHUNK;
kusano fc6ab3
            strm->next_out = out;
kusano fc6ab3
            ret = deflate(strm, last && len == 0 ? Z_FINISH : Z_NO_FLUSH);
kusano fc6ab3
            left = CHUNK - strm->avail_out;
kusano fc6ab3
            while (left) {
kusano fc6ab3
                len = write(gd, out + CHUNK - strm->avail_out - left, left);
kusano fc6ab3
                if (len == -1) bye("writing gzip file", "");
kusano fc6ab3
                left -= (unsigned)len;
kusano fc6ab3
            }
kusano fc6ab3
        } while (strm->avail_out == 0 && ret != Z_STREAM_END);
kusano fc6ab3
    } while (len != 0);
kusano fc6ab3
kusano fc6ab3
    /* write trailer after last entry */
kusano fc6ab3
    if (last) {
kusano fc6ab3
        deflateEnd(strm);
kusano fc6ab3
        out[0] = (unsigned char)(strm->adler);
kusano fc6ab3
        out[1] = (unsigned char)(strm->adler >> 8);
kusano fc6ab3
        out[2] = (unsigned char)(strm->adler >> 16);
kusano fc6ab3
        out[3] = (unsigned char)(strm->adler >> 24);
kusano fc6ab3
        out[4] = (unsigned char)(strm->total_in);
kusano fc6ab3
        out[5] = (unsigned char)(strm->total_in >> 8);
kusano fc6ab3
        out[6] = (unsigned char)(strm->total_in >> 16);
kusano fc6ab3
        out[7] = (unsigned char)(strm->total_in >> 24);
kusano fc6ab3
        len = 8;
kusano fc6ab3
        do {
kusano fc6ab3
            ret = write(gd, out + 8 - len, len);
kusano fc6ab3
            if (ret == -1) bye("writing gzip file", "");
kusano fc6ab3
            len -= ret;
kusano fc6ab3
        } while (len);
kusano fc6ab3
        close(gd);
kusano fc6ab3
    }
kusano fc6ab3
kusano fc6ab3
    /* clean up and return */
kusano fc6ab3
    free(out);
kusano fc6ab3
    free(in);
kusano fc6ab3
    if (fd > 0) close(fd);
kusano fc6ab3
}
kusano fc6ab3
kusano fc6ab3
/* process the compression level option if present, scan the gzip file, and
kusano fc6ab3
   append the specified files, or append the data from stdin if no other file
kusano fc6ab3
   names are provided on the command line -- the gzip file must be writable
kusano fc6ab3
   and seekable */
kusano fc6ab3
int main(int argc, char **argv)
kusano fc6ab3
{
kusano fc6ab3
    int gd, level;
kusano fc6ab3
    z_stream strm;
kusano fc6ab3
kusano fc6ab3
    /* ignore command name */
kusano fc6ab3
    argc--; argv++;
kusano fc6ab3
kusano fc6ab3
    /* provide usage if no arguments */
kusano fc6ab3
    if (*argv == NULL) {
kusano fc6ab3
        printf(
kusano fc6ab3
            "gzappend 1.2 (11 Oct 2012) Copyright (C) 2003, 2012 Mark Adler\n"
kusano fc6ab3
               );
kusano fc6ab3
        printf(
kusano fc6ab3
            "usage: gzappend [-level] file.gz [ addthis [ andthis ... ]]\n");
kusano fc6ab3
        return 0;
kusano fc6ab3
    }
kusano fc6ab3
kusano fc6ab3
    /* set compression level */
kusano fc6ab3
    level = Z_DEFAULT_COMPRESSION;
kusano fc6ab3
    if (argv[0][0] == '-') {
kusano fc6ab3
        if (argv[0][1] < '0' || argv[0][1] > '9' || argv[0][2] != 0)
kusano fc6ab3
            bye("invalid compression level", "");
kusano fc6ab3
        level = argv[0][1] - '0';
kusano fc6ab3
        if (*++argv == NULL) bye("no gzip file name after options", "");
kusano fc6ab3
    }
kusano fc6ab3
kusano fc6ab3
    /* prepare to append to gzip file */
kusano fc6ab3
    gd = gzscan(*argv++, &strm, level);
kusano fc6ab3
kusano fc6ab3
    /* append files on command line, or from stdin if none */
kusano fc6ab3
    if (*argv == NULL)
kusano fc6ab3
        gztack(NULL, gd, &strm, 1);
kusano fc6ab3
    else
kusano fc6ab3
        do {
kusano fc6ab3
            gztack(*argv, gd, &strm, argv[1] == NULL);
kusano fc6ab3
        } while (*++argv != NULL);
kusano fc6ab3
    return 0;
kusano fc6ab3
}