xref: /netbsd-src/external/gpl3/gdb.old/dist/zlib/examples/gzappend.c (revision 6881a4007f077b54e5f51159c52b9b25f57deb0d)
14e00368fSchristos /* gzappend -- command to append to a gzip file
24e00368fSchristos 
3ed8eb4c2Schristos   Copyright (C) 2003, 2012 Mark Adler, all rights reserved
4ed8eb4c2Schristos   version 1.2, 11 Oct 2012
54e00368fSchristos 
64e00368fSchristos   This software is provided 'as-is', without any express or implied
74e00368fSchristos   warranty.  In no event will the author be held liable for any damages
84e00368fSchristos   arising from the use of this software.
94e00368fSchristos 
104e00368fSchristos   Permission is granted to anyone to use this software for any purpose,
114e00368fSchristos   including commercial applications, and to alter it and redistribute it
124e00368fSchristos   freely, subject to the following restrictions:
134e00368fSchristos 
144e00368fSchristos   1. The origin of this software must not be misrepresented; you must not
154e00368fSchristos      claim that you wrote the original software. If you use this software
164e00368fSchristos      in a product, an acknowledgment in the product documentation would be
174e00368fSchristos      appreciated but is not required.
184e00368fSchristos   2. Altered source versions must be plainly marked as such, and must not be
194e00368fSchristos      misrepresented as being the original software.
204e00368fSchristos   3. This notice may not be removed or altered from any source distribution.
214e00368fSchristos 
224e00368fSchristos   Mark Adler    madler@alumni.caltech.edu
234e00368fSchristos  */
244e00368fSchristos 
254e00368fSchristos /*
264e00368fSchristos  * Change history:
274e00368fSchristos  *
284e00368fSchristos  * 1.0  19 Oct 2003     - First version
294e00368fSchristos  * 1.1   4 Nov 2003     - Expand and clarify some comments and notes
304e00368fSchristos  *                      - Add version and copyright to help
314e00368fSchristos  *                      - Send help to stdout instead of stderr
324e00368fSchristos  *                      - Add some preemptive typecasts
334e00368fSchristos  *                      - Add L to constants in lseek() calls
344e00368fSchristos  *                      - Remove some debugging information in error messages
354e00368fSchristos  *                      - Use new data_type definition for zlib 1.2.1
364e00368fSchristos  *                      - Simplfy and unify file operations
374e00368fSchristos  *                      - Finish off gzip file in gztack()
384e00368fSchristos  *                      - Use deflatePrime() instead of adding empty blocks
394e00368fSchristos  *                      - Keep gzip file clean on appended file read errors
404e00368fSchristos  *                      - Use in-place rotate instead of auxiliary buffer
414e00368fSchristos  *                        (Why you ask?  Because it was fun to write!)
42ed8eb4c2Schristos  * 1.2  11 Oct 2012     - Fix for proper z_const usage
43ed8eb4c2Schristos  *                      - Check for input buffer malloc failure
444e00368fSchristos  */
454e00368fSchristos 
464e00368fSchristos /*
474e00368fSchristos    gzappend takes a gzip file and appends to it, compressing files from the
484e00368fSchristos    command line or data from stdin.  The gzip file is written to directly, to
494e00368fSchristos    avoid copying that file, in case it's large.  Note that this results in the
504e00368fSchristos    unfriendly behavior that if gzappend fails, the gzip file is corrupted.
514e00368fSchristos 
524e00368fSchristos    This program was written to illustrate the use of the new Z_BLOCK option of
534e00368fSchristos    zlib 1.2.x's inflate() function.  This option returns from inflate() at each
544e00368fSchristos    block boundary to facilitate locating and modifying the last block bit at
554e00368fSchristos    the start of the final deflate block.  Also whether using Z_BLOCK or not,
564e00368fSchristos    another required feature of zlib 1.2.x is that inflate() now provides the
574e00368fSchristos    number of unusued bits in the last input byte used.  gzappend will not work
584e00368fSchristos    with versions of zlib earlier than 1.2.1.
594e00368fSchristos 
604e00368fSchristos    gzappend first decompresses the gzip file internally, discarding all but
614e00368fSchristos    the last 32K of uncompressed data, and noting the location of the last block
624e00368fSchristos    bit and the number of unused bits in the last byte of the compressed data.
634e00368fSchristos    The gzip trailer containing the CRC-32 and length of the uncompressed data
644e00368fSchristos    is verified.  This trailer will be later overwritten.
654e00368fSchristos 
664e00368fSchristos    Then the last block bit is cleared by seeking back in the file and rewriting
674e00368fSchristos    the byte that contains it.  Seeking forward, the last byte of the compressed
684e00368fSchristos    data is saved along with the number of unused bits to initialize deflate.
694e00368fSchristos 
704e00368fSchristos    A deflate process is initialized, using the last 32K of the uncompressed
714e00368fSchristos    data from the gzip file to initialize the dictionary.  If the total
724e00368fSchristos    uncompressed data was less than 32K, then all of it is used to initialize
734e00368fSchristos    the dictionary.  The deflate output bit buffer is also initialized with the
744e00368fSchristos    last bits from the original deflate stream.  From here on, the data to
754e00368fSchristos    append is simply compressed using deflate, and written to the gzip file.
764e00368fSchristos    When that is complete, the new CRC-32 and uncompressed length are written
774e00368fSchristos    as the trailer of the gzip file.
784e00368fSchristos  */
794e00368fSchristos 
804e00368fSchristos #include <stdio.h>
814e00368fSchristos #include <stdlib.h>
824e00368fSchristos #include <string.h>
834e00368fSchristos #include <fcntl.h>
844e00368fSchristos #include <unistd.h>
854e00368fSchristos #include "zlib.h"
864e00368fSchristos 
874e00368fSchristos #define local static
884e00368fSchristos #define LGCHUNK 14
894e00368fSchristos #define CHUNK (1U << LGCHUNK)
904e00368fSchristos #define DSIZE 32768U
914e00368fSchristos 
924e00368fSchristos /* print an error message and terminate with extreme prejudice */
934e00368fSchristos local void bye(char *msg1, char *msg2)
944e00368fSchristos {
954e00368fSchristos     fprintf(stderr, "gzappend error: %s%s\n", msg1, msg2);
964e00368fSchristos     exit(1);
974e00368fSchristos }
984e00368fSchristos 
994e00368fSchristos /* return the greatest common divisor of a and b using Euclid's algorithm,
1004e00368fSchristos    modified to be fast when one argument much greater than the other, and
1014e00368fSchristos    coded to avoid unnecessary swapping */
1024e00368fSchristos local unsigned gcd(unsigned a, unsigned b)
1034e00368fSchristos {
1044e00368fSchristos     unsigned c;
1054e00368fSchristos 
1064e00368fSchristos     while (a && b)
1074e00368fSchristos         if (a > b) {
1084e00368fSchristos             c = b;
1094e00368fSchristos             while (a - c >= c)
1104e00368fSchristos                 c <<= 1;
1114e00368fSchristos             a -= c;
1124e00368fSchristos         }
1134e00368fSchristos         else {
1144e00368fSchristos             c = a;
1154e00368fSchristos             while (b - c >= c)
1164e00368fSchristos                 c <<= 1;
1174e00368fSchristos             b -= c;
1184e00368fSchristos         }
1194e00368fSchristos     return a + b;
1204e00368fSchristos }
1214e00368fSchristos 
1224e00368fSchristos /* rotate list[0..len-1] left by rot positions, in place */
1234e00368fSchristos local void rotate(unsigned char *list, unsigned len, unsigned rot)
1244e00368fSchristos {
1254e00368fSchristos     unsigned char tmp;
1264e00368fSchristos     unsigned cycles;
1274e00368fSchristos     unsigned char *start, *last, *to, *from;
1284e00368fSchristos 
1294e00368fSchristos     /* normalize rot and handle degenerate cases */
1304e00368fSchristos     if (len < 2) return;
1314e00368fSchristos     if (rot >= len) rot %= len;
1324e00368fSchristos     if (rot == 0) return;
1334e00368fSchristos 
1344e00368fSchristos     /* pointer to last entry in list */
1354e00368fSchristos     last = list + (len - 1);
1364e00368fSchristos 
1374e00368fSchristos     /* do simple left shift by one */
1384e00368fSchristos     if (rot == 1) {
1394e00368fSchristos         tmp = *list;
140*6881a400Schristos         memmove(list, list + 1, len - 1);
1414e00368fSchristos         *last = tmp;
1424e00368fSchristos         return;
1434e00368fSchristos     }
1444e00368fSchristos 
1454e00368fSchristos     /* do simple right shift by one */
1464e00368fSchristos     if (rot == len - 1) {
1474e00368fSchristos         tmp = *last;
1484e00368fSchristos         memmove(list + 1, list, len - 1);
1494e00368fSchristos         *list = tmp;
1504e00368fSchristos         return;
1514e00368fSchristos     }
1524e00368fSchristos 
1534e00368fSchristos     /* otherwise do rotate as a set of cycles in place */
1544e00368fSchristos     cycles = gcd(len, rot);             /* number of cycles */
1554e00368fSchristos     do {
1564e00368fSchristos         start = from = list + cycles;   /* start index is arbitrary */
1574e00368fSchristos         tmp = *from;                    /* save entry to be overwritten */
1584e00368fSchristos         for (;;) {
1594e00368fSchristos             to = from;                  /* next step in cycle */
1604e00368fSchristos             from += rot;                /* go right rot positions */
1614e00368fSchristos             if (from > last) from -= len;   /* (pointer better not wrap) */
1624e00368fSchristos             if (from == start) break;   /* all but one shifted */
1634e00368fSchristos             *to = *from;                /* shift left */
1644e00368fSchristos         }
1654e00368fSchristos         *to = tmp;                      /* complete the circle */
1664e00368fSchristos     } while (--cycles);
1674e00368fSchristos }
1684e00368fSchristos 
1694e00368fSchristos /* structure for gzip file read operations */
1704e00368fSchristos typedef struct {
1714e00368fSchristos     int fd;                     /* file descriptor */
1724e00368fSchristos     int size;                   /* 1 << size is bytes in buf */
1734e00368fSchristos     unsigned left;              /* bytes available at next */
1744e00368fSchristos     unsigned char *buf;         /* buffer */
175ed8eb4c2Schristos     z_const unsigned char *next;    /* next byte in buffer */
1764e00368fSchristos     char *name;                 /* file name for error messages */
1774e00368fSchristos } file;
1784e00368fSchristos 
1794e00368fSchristos /* reload buffer */
1804e00368fSchristos local int readin(file *in)
1814e00368fSchristos {
1824e00368fSchristos     int len;
1834e00368fSchristos 
1844e00368fSchristos     len = read(in->fd, in->buf, 1 << in->size);
1854e00368fSchristos     if (len == -1) bye("error reading ", in->name);
1864e00368fSchristos     in->left = (unsigned)len;
1874e00368fSchristos     in->next = in->buf;
1884e00368fSchristos     return len;
1894e00368fSchristos }
1904e00368fSchristos 
1914e00368fSchristos /* read from file in, exit if end-of-file */
1924e00368fSchristos local int readmore(file *in)
1934e00368fSchristos {
1944e00368fSchristos     if (readin(in) == 0) bye("unexpected end of ", in->name);
1954e00368fSchristos     return 0;
1964e00368fSchristos }
1974e00368fSchristos 
1984e00368fSchristos #define read1(in) (in->left == 0 ? readmore(in) : 0, \
1994e00368fSchristos                    in->left--, *(in->next)++)
2004e00368fSchristos 
2014e00368fSchristos /* skip over n bytes of in */
2024e00368fSchristos local void skip(file *in, unsigned n)
2034e00368fSchristos {
2044e00368fSchristos     unsigned bypass;
2054e00368fSchristos 
2064e00368fSchristos     if (n > in->left) {
2074e00368fSchristos         n -= in->left;
2084e00368fSchristos         bypass = n & ~((1U << in->size) - 1);
2094e00368fSchristos         if (bypass) {
2104e00368fSchristos             if (lseek(in->fd, (off_t)bypass, SEEK_CUR) == -1)
2114e00368fSchristos                 bye("seeking ", in->name);
2124e00368fSchristos             n -= bypass;
2134e00368fSchristos         }
2144e00368fSchristos         readmore(in);
2154e00368fSchristos         if (n > in->left)
2164e00368fSchristos             bye("unexpected end of ", in->name);
2174e00368fSchristos     }
2184e00368fSchristos     in->left -= n;
2194e00368fSchristos     in->next += n;
2204e00368fSchristos }
2214e00368fSchristos 
2224e00368fSchristos /* read a four-byte unsigned integer, little-endian, from in */
2234e00368fSchristos unsigned long read4(file *in)
2244e00368fSchristos {
2254e00368fSchristos     unsigned long val;
2264e00368fSchristos 
2274e00368fSchristos     val = read1(in);
2284e00368fSchristos     val += (unsigned)read1(in) << 8;
2294e00368fSchristos     val += (unsigned long)read1(in) << 16;
2304e00368fSchristos     val += (unsigned long)read1(in) << 24;
2314e00368fSchristos     return val;
2324e00368fSchristos }
2334e00368fSchristos 
2344e00368fSchristos /* skip over gzip header */
2354e00368fSchristos local void gzheader(file *in)
2364e00368fSchristos {
2374e00368fSchristos     int flags;
2384e00368fSchristos     unsigned n;
2394e00368fSchristos 
2404e00368fSchristos     if (read1(in) != 31 || read1(in) != 139) bye(in->name, " not a gzip file");
2414e00368fSchristos     if (read1(in) != 8) bye("unknown compression method in", in->name);
2424e00368fSchristos     flags = read1(in);
2434e00368fSchristos     if (flags & 0xe0) bye("unknown header flags set in", in->name);
2444e00368fSchristos     skip(in, 6);
2454e00368fSchristos     if (flags & 4) {
2464e00368fSchristos         n = read1(in);
2474e00368fSchristos         n += (unsigned)(read1(in)) << 8;
2484e00368fSchristos         skip(in, n);
2494e00368fSchristos     }
2504e00368fSchristos     if (flags & 8) while (read1(in) != 0) ;
2514e00368fSchristos     if (flags & 16) while (read1(in) != 0) ;
2524e00368fSchristos     if (flags & 2) skip(in, 2);
2534e00368fSchristos }
2544e00368fSchristos 
2554e00368fSchristos /* decompress gzip file "name", return strm with a deflate stream ready to
2564e00368fSchristos    continue compression of the data in the gzip file, and return a file
2574e00368fSchristos    descriptor pointing to where to write the compressed data -- the deflate
2584e00368fSchristos    stream is initialized to compress using level "level" */
2594e00368fSchristos local int gzscan(char *name, z_stream *strm, int level)
2604e00368fSchristos {
2614e00368fSchristos     int ret, lastbit, left, full;
2624e00368fSchristos     unsigned have;
2634e00368fSchristos     unsigned long crc, tot;
2644e00368fSchristos     unsigned char *window;
2654e00368fSchristos     off_t lastoff, end;
2664e00368fSchristos     file gz;
2674e00368fSchristos 
2684e00368fSchristos     /* open gzip file */
2694e00368fSchristos     gz.name = name;
2704e00368fSchristos     gz.fd = open(name, O_RDWR, 0);
2714e00368fSchristos     if (gz.fd == -1) bye("cannot open ", name);
2724e00368fSchristos     gz.buf = malloc(CHUNK);
2734e00368fSchristos     if (gz.buf == NULL) bye("out of memory", "");
2744e00368fSchristos     gz.size = LGCHUNK;
2754e00368fSchristos     gz.left = 0;
2764e00368fSchristos 
2774e00368fSchristos     /* skip gzip header */
2784e00368fSchristos     gzheader(&gz);
2794e00368fSchristos 
2804e00368fSchristos     /* prepare to decompress */
2814e00368fSchristos     window = malloc(DSIZE);
2824e00368fSchristos     if (window == NULL) bye("out of memory", "");
2834e00368fSchristos     strm->zalloc = Z_NULL;
2844e00368fSchristos     strm->zfree = Z_NULL;
2854e00368fSchristos     strm->opaque = Z_NULL;
2864e00368fSchristos     ret = inflateInit2(strm, -15);
2874e00368fSchristos     if (ret != Z_OK) bye("out of memory", " or library mismatch");
2884e00368fSchristos 
2894e00368fSchristos     /* decompress the deflate stream, saving append information */
2904e00368fSchristos     lastbit = 0;
2914e00368fSchristos     lastoff = lseek(gz.fd, 0L, SEEK_CUR) - gz.left;
2924e00368fSchristos     left = 0;
2934e00368fSchristos     strm->avail_in = gz.left;
2944e00368fSchristos     strm->next_in = gz.next;
2954e00368fSchristos     crc = crc32(0L, Z_NULL, 0);
2964e00368fSchristos     have = full = 0;
2974e00368fSchristos     do {
2984e00368fSchristos         /* if needed, get more input */
2994e00368fSchristos         if (strm->avail_in == 0) {
3004e00368fSchristos             readmore(&gz);
3014e00368fSchristos             strm->avail_in = gz.left;
3024e00368fSchristos             strm->next_in = gz.next;
3034e00368fSchristos         }
3044e00368fSchristos 
3054e00368fSchristos         /* set up output to next available section of sliding window */
3064e00368fSchristos         strm->avail_out = DSIZE - have;
3074e00368fSchristos         strm->next_out = window + have;
3084e00368fSchristos 
3094e00368fSchristos         /* inflate and check for errors */
3104e00368fSchristos         ret = inflate(strm, Z_BLOCK);
3114e00368fSchristos         if (ret == Z_STREAM_ERROR) bye("internal stream error!", "");
3124e00368fSchristos         if (ret == Z_MEM_ERROR) bye("out of memory", "");
3134e00368fSchristos         if (ret == Z_DATA_ERROR)
3144e00368fSchristos             bye("invalid compressed data--format violated in", name);
3154e00368fSchristos 
3164e00368fSchristos         /* update crc and sliding window pointer */
3174e00368fSchristos         crc = crc32(crc, window + have, DSIZE - have - strm->avail_out);
3184e00368fSchristos         if (strm->avail_out)
3194e00368fSchristos             have = DSIZE - strm->avail_out;
3204e00368fSchristos         else {
3214e00368fSchristos             have = 0;
3224e00368fSchristos             full = 1;
3234e00368fSchristos         }
3244e00368fSchristos 
3254e00368fSchristos         /* process end of block */
3264e00368fSchristos         if (strm->data_type & 128) {
3274e00368fSchristos             if (strm->data_type & 64)
3284e00368fSchristos                 left = strm->data_type & 0x1f;
3294e00368fSchristos             else {
3304e00368fSchristos                 lastbit = strm->data_type & 0x1f;
3314e00368fSchristos                 lastoff = lseek(gz.fd, 0L, SEEK_CUR) - strm->avail_in;
3324e00368fSchristos             }
3334e00368fSchristos         }
3344e00368fSchristos     } while (ret != Z_STREAM_END);
3354e00368fSchristos     inflateEnd(strm);
3364e00368fSchristos     gz.left = strm->avail_in;
3374e00368fSchristos     gz.next = strm->next_in;
3384e00368fSchristos 
3394e00368fSchristos     /* save the location of the end of the compressed data */
3404e00368fSchristos     end = lseek(gz.fd, 0L, SEEK_CUR) - gz.left;
3414e00368fSchristos 
3424e00368fSchristos     /* check gzip trailer and save total for deflate */
3434e00368fSchristos     if (crc != read4(&gz))
3444e00368fSchristos         bye("invalid compressed data--crc mismatch in ", name);
3454e00368fSchristos     tot = strm->total_out;
3464e00368fSchristos     if ((tot & 0xffffffffUL) != read4(&gz))
3474e00368fSchristos         bye("invalid compressed data--length mismatch in", name);
3484e00368fSchristos 
3494e00368fSchristos     /* if not at end of file, warn */
3504e00368fSchristos     if (gz.left || readin(&gz))
3514e00368fSchristos         fprintf(stderr,
3524e00368fSchristos             "gzappend warning: junk at end of gzip file overwritten\n");
3534e00368fSchristos 
3544e00368fSchristos     /* clear last block bit */
3554e00368fSchristos     lseek(gz.fd, lastoff - (lastbit != 0), SEEK_SET);
3564e00368fSchristos     if (read(gz.fd, gz.buf, 1) != 1) bye("reading after seek on ", name);
3574e00368fSchristos     *gz.buf = (unsigned char)(*gz.buf ^ (1 << ((8 - lastbit) & 7)));
3584e00368fSchristos     lseek(gz.fd, -1L, SEEK_CUR);
3594e00368fSchristos     if (write(gz.fd, gz.buf, 1) != 1) bye("writing after seek to ", name);
3604e00368fSchristos 
3614e00368fSchristos     /* if window wrapped, build dictionary from window by rotating */
3624e00368fSchristos     if (full) {
3634e00368fSchristos         rotate(window, DSIZE, have);
3644e00368fSchristos         have = DSIZE;
3654e00368fSchristos     }
3664e00368fSchristos 
3674e00368fSchristos     /* set up deflate stream with window, crc, total_in, and leftover bits */
3684e00368fSchristos     ret = deflateInit2(strm, level, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
3694e00368fSchristos     if (ret != Z_OK) bye("out of memory", "");
3704e00368fSchristos     deflateSetDictionary(strm, window, have);
3714e00368fSchristos     strm->adler = crc;
3724e00368fSchristos     strm->total_in = tot;
3734e00368fSchristos     if (left) {
3744e00368fSchristos         lseek(gz.fd, --end, SEEK_SET);
3754e00368fSchristos         if (read(gz.fd, gz.buf, 1) != 1) bye("reading after seek on ", name);
3764e00368fSchristos         deflatePrime(strm, 8 - left, *gz.buf);
3774e00368fSchristos     }
3784e00368fSchristos     lseek(gz.fd, end, SEEK_SET);
3794e00368fSchristos 
3804e00368fSchristos     /* clean up and return */
3814e00368fSchristos     free(window);
3824e00368fSchristos     free(gz.buf);
3834e00368fSchristos     return gz.fd;
3844e00368fSchristos }
3854e00368fSchristos 
3864e00368fSchristos /* append file "name" to gzip file gd using deflate stream strm -- if last
3874e00368fSchristos    is true, then finish off the deflate stream at the end */
3884e00368fSchristos local void gztack(char *name, int gd, z_stream *strm, int last)
3894e00368fSchristos {
3904e00368fSchristos     int fd, len, ret;
3914e00368fSchristos     unsigned left;
3924e00368fSchristos     unsigned char *in, *out;
3934e00368fSchristos 
3944e00368fSchristos     /* open file to compress and append */
3954e00368fSchristos     fd = 0;
3964e00368fSchristos     if (name != NULL) {
3974e00368fSchristos         fd = open(name, O_RDONLY, 0);
3984e00368fSchristos         if (fd == -1)
3994e00368fSchristos             fprintf(stderr, "gzappend warning: %s not found, skipping ...\n",
4004e00368fSchristos                     name);
4014e00368fSchristos     }
4024e00368fSchristos 
4034e00368fSchristos     /* allocate buffers */
404ed8eb4c2Schristos     in = malloc(CHUNK);
4054e00368fSchristos     out = malloc(CHUNK);
406ed8eb4c2Schristos     if (in == NULL || out == NULL) bye("out of memory", "");
4074e00368fSchristos 
4084e00368fSchristos     /* compress input file and append to gzip file */
4094e00368fSchristos     do {
4104e00368fSchristos         /* get more input */
411ed8eb4c2Schristos         len = read(fd, in, CHUNK);
4124e00368fSchristos         if (len == -1) {
4134e00368fSchristos             fprintf(stderr,
4144e00368fSchristos                     "gzappend warning: error reading %s, skipping rest ...\n",
4154e00368fSchristos                     name);
4164e00368fSchristos             len = 0;
4174e00368fSchristos         }
4184e00368fSchristos         strm->avail_in = (unsigned)len;
4194e00368fSchristos         strm->next_in = in;
4204e00368fSchristos         if (len) strm->adler = crc32(strm->adler, in, (unsigned)len);
4214e00368fSchristos 
4224e00368fSchristos         /* compress and write all available output */
4234e00368fSchristos         do {
4244e00368fSchristos             strm->avail_out = CHUNK;
4254e00368fSchristos             strm->next_out = out;
4264e00368fSchristos             ret = deflate(strm, last && len == 0 ? Z_FINISH : Z_NO_FLUSH);
4274e00368fSchristos             left = CHUNK - strm->avail_out;
4284e00368fSchristos             while (left) {
4294e00368fSchristos                 len = write(gd, out + CHUNK - strm->avail_out - left, left);
4304e00368fSchristos                 if (len == -1) bye("writing gzip file", "");
4314e00368fSchristos                 left -= (unsigned)len;
4324e00368fSchristos             }
4334e00368fSchristos         } while (strm->avail_out == 0 && ret != Z_STREAM_END);
4344e00368fSchristos     } while (len != 0);
4354e00368fSchristos 
4364e00368fSchristos     /* write trailer after last entry */
4374e00368fSchristos     if (last) {
4384e00368fSchristos         deflateEnd(strm);
4394e00368fSchristos         out[0] = (unsigned char)(strm->adler);
4404e00368fSchristos         out[1] = (unsigned char)(strm->adler >> 8);
4414e00368fSchristos         out[2] = (unsigned char)(strm->adler >> 16);
4424e00368fSchristos         out[3] = (unsigned char)(strm->adler >> 24);
4434e00368fSchristos         out[4] = (unsigned char)(strm->total_in);
4444e00368fSchristos         out[5] = (unsigned char)(strm->total_in >> 8);
4454e00368fSchristos         out[6] = (unsigned char)(strm->total_in >> 16);
4464e00368fSchristos         out[7] = (unsigned char)(strm->total_in >> 24);
4474e00368fSchristos         len = 8;
4484e00368fSchristos         do {
4494e00368fSchristos             ret = write(gd, out + 8 - len, len);
4504e00368fSchristos             if (ret == -1) bye("writing gzip file", "");
4514e00368fSchristos             len -= ret;
4524e00368fSchristos         } while (len);
4534e00368fSchristos         close(gd);
4544e00368fSchristos     }
4554e00368fSchristos 
4564e00368fSchristos     /* clean up and return */
4574e00368fSchristos     free(out);
458ed8eb4c2Schristos     free(in);
4594e00368fSchristos     if (fd > 0) close(fd);
4604e00368fSchristos }
4614e00368fSchristos 
4624e00368fSchristos /* process the compression level option if present, scan the gzip file, and
4634e00368fSchristos    append the specified files, or append the data from stdin if no other file
4644e00368fSchristos    names are provided on the command line -- the gzip file must be writable
4654e00368fSchristos    and seekable */
4664e00368fSchristos int main(int argc, char **argv)
4674e00368fSchristos {
4684e00368fSchristos     int gd, level;
4694e00368fSchristos     z_stream strm;
4704e00368fSchristos 
4714e00368fSchristos     /* ignore command name */
472ed8eb4c2Schristos     argc--; argv++;
4734e00368fSchristos 
4744e00368fSchristos     /* provide usage if no arguments */
4754e00368fSchristos     if (*argv == NULL) {
476ed8eb4c2Schristos         printf(
477ed8eb4c2Schristos             "gzappend 1.2 (11 Oct 2012) Copyright (C) 2003, 2012 Mark Adler\n"
478ed8eb4c2Schristos                );
4794e00368fSchristos         printf(
4804e00368fSchristos             "usage: gzappend [-level] file.gz [ addthis [ andthis ... ]]\n");
4814e00368fSchristos         return 0;
4824e00368fSchristos     }
4834e00368fSchristos 
4844e00368fSchristos     /* set compression level */
4854e00368fSchristos     level = Z_DEFAULT_COMPRESSION;
4864e00368fSchristos     if (argv[0][0] == '-') {
4874e00368fSchristos         if (argv[0][1] < '0' || argv[0][1] > '9' || argv[0][2] != 0)
4884e00368fSchristos             bye("invalid compression level", "");
4894e00368fSchristos         level = argv[0][1] - '0';
4904e00368fSchristos         if (*++argv == NULL) bye("no gzip file name after options", "");
4914e00368fSchristos     }
4924e00368fSchristos 
4934e00368fSchristos     /* prepare to append to gzip file */
4944e00368fSchristos     gd = gzscan(*argv++, &strm, level);
4954e00368fSchristos 
4964e00368fSchristos     /* append files on command line, or from stdin if none */
4974e00368fSchristos     if (*argv == NULL)
4984e00368fSchristos         gztack(NULL, gd, &strm, 1);
4994e00368fSchristos     else
5004e00368fSchristos         do {
5014e00368fSchristos             gztack(*argv, gd, &strm, argv[1] == NULL);
5024e00368fSchristos         } while (*++argv != NULL);
5034e00368fSchristos     return 0;
5044e00368fSchristos }
505