15ba6b03cSchristos /* gzappend -- command to append to a gzip file
25ba6b03cSchristos 
375f9f1baSchristos   Copyright (C) 2003, 2012 Mark Adler, all rights reserved
475f9f1baSchristos   version 1.2, 11 Oct 2012
55ba6b03cSchristos 
65ba6b03cSchristos   This software is provided 'as-is', without any express or implied
75ba6b03cSchristos   warranty.  In no event will the author be held liable for any damages
85ba6b03cSchristos   arising from the use of this software.
95ba6b03cSchristos 
105ba6b03cSchristos   Permission is granted to anyone to use this software for any purpose,
115ba6b03cSchristos   including commercial applications, and to alter it and redistribute it
125ba6b03cSchristos   freely, subject to the following restrictions:
135ba6b03cSchristos 
145ba6b03cSchristos   1. The origin of this software must not be misrepresented; you must not
155ba6b03cSchristos      claim that you wrote the original software. If you use this software
165ba6b03cSchristos      in a product, an acknowledgment in the product documentation would be
175ba6b03cSchristos      appreciated but is not required.
185ba6b03cSchristos   2. Altered source versions must be plainly marked as such, and must not be
195ba6b03cSchristos      misrepresented as being the original software.
205ba6b03cSchristos   3. This notice may not be removed or altered from any source distribution.
215ba6b03cSchristos 
225ba6b03cSchristos   Mark Adler    madler@alumni.caltech.edu
235ba6b03cSchristos  */
245ba6b03cSchristos 
255ba6b03cSchristos /*
265ba6b03cSchristos  * Change history:
275ba6b03cSchristos  *
285ba6b03cSchristos  * 1.0  19 Oct 2003     - First version
295ba6b03cSchristos  * 1.1   4 Nov 2003     - Expand and clarify some comments and notes
305ba6b03cSchristos  *                      - Add version and copyright to help
315ba6b03cSchristos  *                      - Send help to stdout instead of stderr
325ba6b03cSchristos  *                      - Add some preemptive typecasts
335ba6b03cSchristos  *                      - Add L to constants in lseek() calls
345ba6b03cSchristos  *                      - Remove some debugging information in error messages
355ba6b03cSchristos  *                      - Use new data_type definition for zlib 1.2.1
365ba6b03cSchristos  *                      - Simplfy and unify file operations
375ba6b03cSchristos  *                      - Finish off gzip file in gztack()
385ba6b03cSchristos  *                      - Use deflatePrime() instead of adding empty blocks
395ba6b03cSchristos  *                      - Keep gzip file clean on appended file read errors
405ba6b03cSchristos  *                      - Use in-place rotate instead of auxiliary buffer
415ba6b03cSchristos  *                        (Why you ask?  Because it was fun to write!)
4275f9f1baSchristos  * 1.2  11 Oct 2012     - Fix for proper z_const usage
4375f9f1baSchristos  *                      - Check for input buffer malloc failure
445ba6b03cSchristos  */
455ba6b03cSchristos 
465ba6b03cSchristos /*
475ba6b03cSchristos    gzappend takes a gzip file and appends to it, compressing files from the
485ba6b03cSchristos    command line or data from stdin.  The gzip file is written to directly, to
495ba6b03cSchristos    avoid copying that file, in case it's large.  Note that this results in the
505ba6b03cSchristos    unfriendly behavior that if gzappend fails, the gzip file is corrupted.
515ba6b03cSchristos 
525ba6b03cSchristos    This program was written to illustrate the use of the new Z_BLOCK option of
535ba6b03cSchristos    zlib 1.2.x's inflate() function.  This option returns from inflate() at each
545ba6b03cSchristos    block boundary to facilitate locating and modifying the last block bit at
555ba6b03cSchristos    the start of the final deflate block.  Also whether using Z_BLOCK or not,
565ba6b03cSchristos    another required feature of zlib 1.2.x is that inflate() now provides the
575ba6b03cSchristos    number of unusued bits in the last input byte used.  gzappend will not work
585ba6b03cSchristos    with versions of zlib earlier than 1.2.1.
595ba6b03cSchristos 
605ba6b03cSchristos    gzappend first decompresses the gzip file internally, discarding all but
615ba6b03cSchristos    the last 32K of uncompressed data, and noting the location of the last block
625ba6b03cSchristos    bit and the number of unused bits in the last byte of the compressed data.
635ba6b03cSchristos    The gzip trailer containing the CRC-32 and length of the uncompressed data
645ba6b03cSchristos    is verified.  This trailer will be later overwritten.
655ba6b03cSchristos 
665ba6b03cSchristos    Then the last block bit is cleared by seeking back in the file and rewriting
675ba6b03cSchristos    the byte that contains it.  Seeking forward, the last byte of the compressed
685ba6b03cSchristos    data is saved along with the number of unused bits to initialize deflate.
695ba6b03cSchristos 
705ba6b03cSchristos    A deflate process is initialized, using the last 32K of the uncompressed
715ba6b03cSchristos    data from the gzip file to initialize the dictionary.  If the total
725ba6b03cSchristos    uncompressed data was less than 32K, then all of it is used to initialize
735ba6b03cSchristos    the dictionary.  The deflate output bit buffer is also initialized with the
745ba6b03cSchristos    last bits from the original deflate stream.  From here on, the data to
755ba6b03cSchristos    append is simply compressed using deflate, and written to the gzip file.
765ba6b03cSchristos    When that is complete, the new CRC-32 and uncompressed length are written
775ba6b03cSchristos    as the trailer of the gzip file.
785ba6b03cSchristos  */
795ba6b03cSchristos 
805ba6b03cSchristos #include <stdio.h>
815ba6b03cSchristos #include <stdlib.h>
825ba6b03cSchristos #include <string.h>
835ba6b03cSchristos #include <fcntl.h>
845ba6b03cSchristos #include <unistd.h>
855ba6b03cSchristos #include "zlib.h"
865ba6b03cSchristos 
875ba6b03cSchristos #define local static
885ba6b03cSchristos #define LGCHUNK 14
895ba6b03cSchristos #define CHUNK (1U << LGCHUNK)
905ba6b03cSchristos #define DSIZE 32768U
915ba6b03cSchristos 
925ba6b03cSchristos /* print an error message and terminate with extreme prejudice */
bye(char * msg1,char * msg2)935ba6b03cSchristos local void bye(char *msg1, char *msg2)
945ba6b03cSchristos {
955ba6b03cSchristos     fprintf(stderr, "gzappend error: %s%s\n", msg1, msg2);
965ba6b03cSchristos     exit(1);
975ba6b03cSchristos }
985ba6b03cSchristos 
995ba6b03cSchristos /* return the greatest common divisor of a and b using Euclid's algorithm,
1005ba6b03cSchristos    modified to be fast when one argument much greater than the other, and
1015ba6b03cSchristos    coded to avoid unnecessary swapping */
gcd(unsigned a,unsigned b)1025ba6b03cSchristos local unsigned gcd(unsigned a, unsigned b)
1035ba6b03cSchristos {
1045ba6b03cSchristos     unsigned c;
1055ba6b03cSchristos 
1065ba6b03cSchristos     while (a && b)
1075ba6b03cSchristos         if (a > b) {
1085ba6b03cSchristos             c = b;
1095ba6b03cSchristos             while (a - c >= c)
1105ba6b03cSchristos                 c <<= 1;
1115ba6b03cSchristos             a -= c;
1125ba6b03cSchristos         }
1135ba6b03cSchristos         else {
1145ba6b03cSchristos             c = a;
1155ba6b03cSchristos             while (b - c >= c)
1165ba6b03cSchristos                 c <<= 1;
1175ba6b03cSchristos             b -= c;
1185ba6b03cSchristos         }
1195ba6b03cSchristos     return a + b;
1205ba6b03cSchristos }
1215ba6b03cSchristos 
1225ba6b03cSchristos /* rotate list[0..len-1] left by rot positions, in place */
rotate(unsigned char * list,unsigned len,unsigned rot)1235ba6b03cSchristos local void rotate(unsigned char *list, unsigned len, unsigned rot)
1245ba6b03cSchristos {
1255ba6b03cSchristos     unsigned char tmp;
1265ba6b03cSchristos     unsigned cycles;
1275ba6b03cSchristos     unsigned char *start, *last, *to, *from;
1285ba6b03cSchristos 
1295ba6b03cSchristos     /* normalize rot and handle degenerate cases */
1305ba6b03cSchristos     if (len < 2) return;
1315ba6b03cSchristos     if (rot >= len) rot %= len;
1325ba6b03cSchristos     if (rot == 0) return;
1335ba6b03cSchristos 
1345ba6b03cSchristos     /* pointer to last entry in list */
1355ba6b03cSchristos     last = list + (len - 1);
1365ba6b03cSchristos 
1375ba6b03cSchristos     /* do simple left shift by one */
1385ba6b03cSchristos     if (rot == 1) {
1395ba6b03cSchristos         tmp = *list;
140*f22f0ef4Schristos         memmove(list, list + 1, len - 1);
1415ba6b03cSchristos         *last = tmp;
1425ba6b03cSchristos         return;
1435ba6b03cSchristos     }
1445ba6b03cSchristos 
1455ba6b03cSchristos     /* do simple right shift by one */
1465ba6b03cSchristos     if (rot == len - 1) {
1475ba6b03cSchristos         tmp = *last;
1485ba6b03cSchristos         memmove(list + 1, list, len - 1);
1495ba6b03cSchristos         *list = tmp;
1505ba6b03cSchristos         return;
1515ba6b03cSchristos     }
1525ba6b03cSchristos 
1535ba6b03cSchristos     /* otherwise do rotate as a set of cycles in place */
1545ba6b03cSchristos     cycles = gcd(len, rot);             /* number of cycles */
1555ba6b03cSchristos     do {
1565ba6b03cSchristos         start = from = list + cycles;   /* start index is arbitrary */
1575ba6b03cSchristos         tmp = *from;                    /* save entry to be overwritten */
1585ba6b03cSchristos         for (;;) {
1595ba6b03cSchristos             to = from;                  /* next step in cycle */
1605ba6b03cSchristos             from += rot;                /* go right rot positions */
1615ba6b03cSchristos             if (from > last) from -= len;   /* (pointer better not wrap) */
1625ba6b03cSchristos             if (from == start) break;   /* all but one shifted */
1635ba6b03cSchristos             *to = *from;                /* shift left */
1645ba6b03cSchristos         }
1655ba6b03cSchristos         *to = tmp;                      /* complete the circle */
1665ba6b03cSchristos     } while (--cycles);
1675ba6b03cSchristos }
1685ba6b03cSchristos 
1695ba6b03cSchristos /* structure for gzip file read operations */
1705ba6b03cSchristos typedef struct {
1715ba6b03cSchristos     int fd;                     /* file descriptor */
1725ba6b03cSchristos     int size;                   /* 1 << size is bytes in buf */
1735ba6b03cSchristos     unsigned left;              /* bytes available at next */
1745ba6b03cSchristos     unsigned char *buf;         /* buffer */
17575f9f1baSchristos     z_const unsigned char *next;    /* next byte in buffer */
1765ba6b03cSchristos     char *name;                 /* file name for error messages */
1775ba6b03cSchristos } file;
1785ba6b03cSchristos 
1795ba6b03cSchristos /* reload buffer */
readin(file * in)1805ba6b03cSchristos local int readin(file *in)
1815ba6b03cSchristos {
1825ba6b03cSchristos     int len;
1835ba6b03cSchristos 
1845ba6b03cSchristos     len = read(in->fd, in->buf, 1 << in->size);
1855ba6b03cSchristos     if (len == -1) bye("error reading ", in->name);
1865ba6b03cSchristos     in->left = (unsigned)len;
1875ba6b03cSchristos     in->next = in->buf;
1885ba6b03cSchristos     return len;
1895ba6b03cSchristos }
1905ba6b03cSchristos 
1915ba6b03cSchristos /* read from file in, exit if end-of-file */
readmore(file * in)1925ba6b03cSchristos local int readmore(file *in)
1935ba6b03cSchristos {
1945ba6b03cSchristos     if (readin(in) == 0) bye("unexpected end of ", in->name);
1955ba6b03cSchristos     return 0;
1965ba6b03cSchristos }
1975ba6b03cSchristos 
1985ba6b03cSchristos #define read1(in) (in->left == 0 ? readmore(in) : 0, \
1995ba6b03cSchristos                    in->left--, *(in->next)++)
2005ba6b03cSchristos 
2015ba6b03cSchristos /* skip over n bytes of in */
skip(file * in,unsigned n)2025ba6b03cSchristos local void skip(file *in, unsigned n)
2035ba6b03cSchristos {
2045ba6b03cSchristos     unsigned bypass;
2055ba6b03cSchristos 
2065ba6b03cSchristos     if (n > in->left) {
2075ba6b03cSchristos         n -= in->left;
2085ba6b03cSchristos         bypass = n & ~((1U << in->size) - 1);
2095ba6b03cSchristos         if (bypass) {
2105ba6b03cSchristos             if (lseek(in->fd, (off_t)bypass, SEEK_CUR) == -1)
2115ba6b03cSchristos                 bye("seeking ", in->name);
2125ba6b03cSchristos             n -= bypass;
2135ba6b03cSchristos         }
2145ba6b03cSchristos         readmore(in);
2155ba6b03cSchristos         if (n > in->left)
2165ba6b03cSchristos             bye("unexpected end of ", in->name);
2175ba6b03cSchristos     }
2185ba6b03cSchristos     in->left -= n;
2195ba6b03cSchristos     in->next += n;
2205ba6b03cSchristos }
2215ba6b03cSchristos 
2225ba6b03cSchristos /* read a four-byte unsigned integer, little-endian, from in */
read4(file * in)2235ba6b03cSchristos unsigned long read4(file *in)
2245ba6b03cSchristos {
2255ba6b03cSchristos     unsigned long val;
2265ba6b03cSchristos 
2275ba6b03cSchristos     val = read1(in);
2285ba6b03cSchristos     val += (unsigned)read1(in) << 8;
2295ba6b03cSchristos     val += (unsigned long)read1(in) << 16;
2305ba6b03cSchristos     val += (unsigned long)read1(in) << 24;
2315ba6b03cSchristos     return val;
2325ba6b03cSchristos }
2335ba6b03cSchristos 
2345ba6b03cSchristos /* skip over gzip header */
gzheader(file * in)2355ba6b03cSchristos local void gzheader(file *in)
2365ba6b03cSchristos {
2375ba6b03cSchristos     int flags;
2385ba6b03cSchristos     unsigned n;
2395ba6b03cSchristos 
2405ba6b03cSchristos     if (read1(in) != 31 || read1(in) != 139) bye(in->name, " not a gzip file");
2415ba6b03cSchristos     if (read1(in) != 8) bye("unknown compression method in", in->name);
2425ba6b03cSchristos     flags = read1(in);
2435ba6b03cSchristos     if (flags & 0xe0) bye("unknown header flags set in", in->name);
2445ba6b03cSchristos     skip(in, 6);
2455ba6b03cSchristos     if (flags & 4) {
2465ba6b03cSchristos         n = read1(in);
2475ba6b03cSchristos         n += (unsigned)(read1(in)) << 8;
2485ba6b03cSchristos         skip(in, n);
2495ba6b03cSchristos     }
2505ba6b03cSchristos     if (flags & 8) while (read1(in) != 0) ;
2515ba6b03cSchristos     if (flags & 16) while (read1(in) != 0) ;
2525ba6b03cSchristos     if (flags & 2) skip(in, 2);
2535ba6b03cSchristos }
2545ba6b03cSchristos 
2555ba6b03cSchristos /* decompress gzip file "name", return strm with a deflate stream ready to
2565ba6b03cSchristos    continue compression of the data in the gzip file, and return a file
2575ba6b03cSchristos    descriptor pointing to where to write the compressed data -- the deflate
2585ba6b03cSchristos    stream is initialized to compress using level "level" */
gzscan(char * name,z_stream * strm,int level)2595ba6b03cSchristos local int gzscan(char *name, z_stream *strm, int level)
2605ba6b03cSchristos {
2615ba6b03cSchristos     int ret, lastbit, left, full;
2625ba6b03cSchristos     unsigned have;
2635ba6b03cSchristos     unsigned long crc, tot;
2645ba6b03cSchristos     unsigned char *window;
2655ba6b03cSchristos     off_t lastoff, end;
2665ba6b03cSchristos     file gz;
2675ba6b03cSchristos 
2685ba6b03cSchristos     /* open gzip file */
2695ba6b03cSchristos     gz.name = name;
2705ba6b03cSchristos     gz.fd = open(name, O_RDWR, 0);
2715ba6b03cSchristos     if (gz.fd == -1) bye("cannot open ", name);
2725ba6b03cSchristos     gz.buf = malloc(CHUNK);
2735ba6b03cSchristos     if (gz.buf == NULL) bye("out of memory", "");
2745ba6b03cSchristos     gz.size = LGCHUNK;
2755ba6b03cSchristos     gz.left = 0;
2765ba6b03cSchristos 
2775ba6b03cSchristos     /* skip gzip header */
2785ba6b03cSchristos     gzheader(&gz);
2795ba6b03cSchristos 
2805ba6b03cSchristos     /* prepare to decompress */
2815ba6b03cSchristos     window = malloc(DSIZE);
2825ba6b03cSchristos     if (window == NULL) bye("out of memory", "");
2835ba6b03cSchristos     strm->zalloc = Z_NULL;
2845ba6b03cSchristos     strm->zfree = Z_NULL;
2855ba6b03cSchristos     strm->opaque = Z_NULL;
2865ba6b03cSchristos     ret = inflateInit2(strm, -15);
2875ba6b03cSchristos     if (ret != Z_OK) bye("out of memory", " or library mismatch");
2885ba6b03cSchristos 
2895ba6b03cSchristos     /* decompress the deflate stream, saving append information */
2905ba6b03cSchristos     lastbit = 0;
2915ba6b03cSchristos     lastoff = lseek(gz.fd, 0L, SEEK_CUR) - gz.left;
2925ba6b03cSchristos     left = 0;
2935ba6b03cSchristos     strm->avail_in = gz.left;
2945ba6b03cSchristos     strm->next_in = gz.next;
2955ba6b03cSchristos     crc = crc32(0L, Z_NULL, 0);
2965ba6b03cSchristos     have = full = 0;
2975ba6b03cSchristos     do {
2985ba6b03cSchristos         /* if needed, get more input */
2995ba6b03cSchristos         if (strm->avail_in == 0) {
3005ba6b03cSchristos             readmore(&gz);
3015ba6b03cSchristos             strm->avail_in = gz.left;
3025ba6b03cSchristos             strm->next_in = gz.next;
3035ba6b03cSchristos         }
3045ba6b03cSchristos 
3055ba6b03cSchristos         /* set up output to next available section of sliding window */
3065ba6b03cSchristos         strm->avail_out = DSIZE - have;
3075ba6b03cSchristos         strm->next_out = window + have;
3085ba6b03cSchristos 
3095ba6b03cSchristos         /* inflate and check for errors */
3105ba6b03cSchristos         ret = inflate(strm, Z_BLOCK);
3115ba6b03cSchristos         if (ret == Z_STREAM_ERROR) bye("internal stream error!", "");
3125ba6b03cSchristos         if (ret == Z_MEM_ERROR) bye("out of memory", "");
3135ba6b03cSchristos         if (ret == Z_DATA_ERROR)
3145ba6b03cSchristos             bye("invalid compressed data--format violated in", name);
3155ba6b03cSchristos 
3165ba6b03cSchristos         /* update crc and sliding window pointer */
3175ba6b03cSchristos         crc = crc32(crc, window + have, DSIZE - have - strm->avail_out);
3185ba6b03cSchristos         if (strm->avail_out)
3195ba6b03cSchristos             have = DSIZE - strm->avail_out;
3205ba6b03cSchristos         else {
3215ba6b03cSchristos             have = 0;
3225ba6b03cSchristos             full = 1;
3235ba6b03cSchristos         }
3245ba6b03cSchristos 
3255ba6b03cSchristos         /* process end of block */
3265ba6b03cSchristos         if (strm->data_type & 128) {
3275ba6b03cSchristos             if (strm->data_type & 64)
3285ba6b03cSchristos                 left = strm->data_type & 0x1f;
3295ba6b03cSchristos             else {
3305ba6b03cSchristos                 lastbit = strm->data_type & 0x1f;
3315ba6b03cSchristos                 lastoff = lseek(gz.fd, 0L, SEEK_CUR) - strm->avail_in;
3325ba6b03cSchristos             }
3335ba6b03cSchristos         }
3345ba6b03cSchristos     } while (ret != Z_STREAM_END);
3355ba6b03cSchristos     inflateEnd(strm);
3365ba6b03cSchristos     gz.left = strm->avail_in;
3375ba6b03cSchristos     gz.next = strm->next_in;
3385ba6b03cSchristos 
3395ba6b03cSchristos     /* save the location of the end of the compressed data */
3405ba6b03cSchristos     end = lseek(gz.fd, 0L, SEEK_CUR) - gz.left;
3415ba6b03cSchristos 
3425ba6b03cSchristos     /* check gzip trailer and save total for deflate */
3435ba6b03cSchristos     if (crc != read4(&gz))
3445ba6b03cSchristos         bye("invalid compressed data--crc mismatch in ", name);
3455ba6b03cSchristos     tot = strm->total_out;
3465ba6b03cSchristos     if ((tot & 0xffffffffUL) != read4(&gz))
3475ba6b03cSchristos         bye("invalid compressed data--length mismatch in", name);
3485ba6b03cSchristos 
3495ba6b03cSchristos     /* if not at end of file, warn */
3505ba6b03cSchristos     if (gz.left || readin(&gz))
3515ba6b03cSchristos         fprintf(stderr,
3525ba6b03cSchristos             "gzappend warning: junk at end of gzip file overwritten\n");
3535ba6b03cSchristos 
3545ba6b03cSchristos     /* clear last block bit */
3555ba6b03cSchristos     lseek(gz.fd, lastoff - (lastbit != 0), SEEK_SET);
3565ba6b03cSchristos     if (read(gz.fd, gz.buf, 1) != 1) bye("reading after seek on ", name);
3575ba6b03cSchristos     *gz.buf = (unsigned char)(*gz.buf ^ (1 << ((8 - lastbit) & 7)));
3585ba6b03cSchristos     lseek(gz.fd, -1L, SEEK_CUR);
3595ba6b03cSchristos     if (write(gz.fd, gz.buf, 1) != 1) bye("writing after seek to ", name);
3605ba6b03cSchristos 
3615ba6b03cSchristos     /* if window wrapped, build dictionary from window by rotating */
3625ba6b03cSchristos     if (full) {
3635ba6b03cSchristos         rotate(window, DSIZE, have);
3645ba6b03cSchristos         have = DSIZE;
3655ba6b03cSchristos     }
3665ba6b03cSchristos 
3675ba6b03cSchristos     /* set up deflate stream with window, crc, total_in, and leftover bits */
3685ba6b03cSchristos     ret = deflateInit2(strm, level, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
3695ba6b03cSchristos     if (ret != Z_OK) bye("out of memory", "");
3705ba6b03cSchristos     deflateSetDictionary(strm, window, have);
3715ba6b03cSchristos     strm->adler = crc;
3725ba6b03cSchristos     strm->total_in = tot;
3735ba6b03cSchristos     if (left) {
3745ba6b03cSchristos         lseek(gz.fd, --end, SEEK_SET);
3755ba6b03cSchristos         if (read(gz.fd, gz.buf, 1) != 1) bye("reading after seek on ", name);
3765ba6b03cSchristos         deflatePrime(strm, 8 - left, *gz.buf);
3775ba6b03cSchristos     }
3785ba6b03cSchristos     lseek(gz.fd, end, SEEK_SET);
3795ba6b03cSchristos 
3805ba6b03cSchristos     /* clean up and return */
3815ba6b03cSchristos     free(window);
3825ba6b03cSchristos     free(gz.buf);
3835ba6b03cSchristos     return gz.fd;
3845ba6b03cSchristos }
3855ba6b03cSchristos 
3865ba6b03cSchristos /* append file "name" to gzip file gd using deflate stream strm -- if last
3875ba6b03cSchristos    is true, then finish off the deflate stream at the end */
gztack(char * name,int gd,z_stream * strm,int last)3885ba6b03cSchristos local void gztack(char *name, int gd, z_stream *strm, int last)
3895ba6b03cSchristos {
3905ba6b03cSchristos     int fd, len, ret;
3915ba6b03cSchristos     unsigned left;
3925ba6b03cSchristos     unsigned char *in, *out;
3935ba6b03cSchristos 
3945ba6b03cSchristos     /* open file to compress and append */
3955ba6b03cSchristos     fd = 0;
3965ba6b03cSchristos     if (name != NULL) {
3975ba6b03cSchristos         fd = open(name, O_RDONLY, 0);
3985ba6b03cSchristos         if (fd == -1)
3995ba6b03cSchristos             fprintf(stderr, "gzappend warning: %s not found, skipping ...\n",
4005ba6b03cSchristos                     name);
4015ba6b03cSchristos     }
4025ba6b03cSchristos 
4035ba6b03cSchristos     /* allocate buffers */
40475f9f1baSchristos     in = malloc(CHUNK);
4055ba6b03cSchristos     out = malloc(CHUNK);
40675f9f1baSchristos     if (in == NULL || out == NULL) bye("out of memory", "");
4075ba6b03cSchristos 
4085ba6b03cSchristos     /* compress input file and append to gzip file */
4095ba6b03cSchristos     do {
4105ba6b03cSchristos         /* get more input */
41175f9f1baSchristos         len = read(fd, in, CHUNK);
4125ba6b03cSchristos         if (len == -1) {
4135ba6b03cSchristos             fprintf(stderr,
4145ba6b03cSchristos                     "gzappend warning: error reading %s, skipping rest ...\n",
4155ba6b03cSchristos                     name);
4165ba6b03cSchristos             len = 0;
4175ba6b03cSchristos         }
4185ba6b03cSchristos         strm->avail_in = (unsigned)len;
4195ba6b03cSchristos         strm->next_in = in;
4205ba6b03cSchristos         if (len) strm->adler = crc32(strm->adler, in, (unsigned)len);
4215ba6b03cSchristos 
4225ba6b03cSchristos         /* compress and write all available output */
4235ba6b03cSchristos         do {
4245ba6b03cSchristos             strm->avail_out = CHUNK;
4255ba6b03cSchristos             strm->next_out = out;
4265ba6b03cSchristos             ret = deflate(strm, last && len == 0 ? Z_FINISH : Z_NO_FLUSH);
4275ba6b03cSchristos             left = CHUNK - strm->avail_out;
4285ba6b03cSchristos             while (left) {
4295ba6b03cSchristos                 len = write(gd, out + CHUNK - strm->avail_out - left, left);
4305ba6b03cSchristos                 if (len == -1) bye("writing gzip file", "");
4315ba6b03cSchristos                 left -= (unsigned)len;
4325ba6b03cSchristos             }
4335ba6b03cSchristos         } while (strm->avail_out == 0 && ret != Z_STREAM_END);
4345ba6b03cSchristos     } while (len != 0);
4355ba6b03cSchristos 
4365ba6b03cSchristos     /* write trailer after last entry */
4375ba6b03cSchristos     if (last) {
4385ba6b03cSchristos         deflateEnd(strm);
4395ba6b03cSchristos         out[0] = (unsigned char)(strm->adler);
4405ba6b03cSchristos         out[1] = (unsigned char)(strm->adler >> 8);
4415ba6b03cSchristos         out[2] = (unsigned char)(strm->adler >> 16);
4425ba6b03cSchristos         out[3] = (unsigned char)(strm->adler >> 24);
4435ba6b03cSchristos         out[4] = (unsigned char)(strm->total_in);
4445ba6b03cSchristos         out[5] = (unsigned char)(strm->total_in >> 8);
4455ba6b03cSchristos         out[6] = (unsigned char)(strm->total_in >> 16);
4465ba6b03cSchristos         out[7] = (unsigned char)(strm->total_in >> 24);
4475ba6b03cSchristos         len = 8;
4485ba6b03cSchristos         do {
4495ba6b03cSchristos             ret = write(gd, out + 8 - len, len);
4505ba6b03cSchristos             if (ret == -1) bye("writing gzip file", "");
4515ba6b03cSchristos             len -= ret;
4525ba6b03cSchristos         } while (len);
4535ba6b03cSchristos         close(gd);
4545ba6b03cSchristos     }
4555ba6b03cSchristos 
4565ba6b03cSchristos     /* clean up and return */
4575ba6b03cSchristos     free(out);
45875f9f1baSchristos     free(in);
4595ba6b03cSchristos     if (fd > 0) close(fd);
4605ba6b03cSchristos }
4615ba6b03cSchristos 
4625ba6b03cSchristos /* process the compression level option if present, scan the gzip file, and
4635ba6b03cSchristos    append the specified files, or append the data from stdin if no other file
4645ba6b03cSchristos    names are provided on the command line -- the gzip file must be writable
4655ba6b03cSchristos    and seekable */
main(int argc,char ** argv)4665ba6b03cSchristos int main(int argc, char **argv)
4675ba6b03cSchristos {
4685ba6b03cSchristos     int gd, level;
4695ba6b03cSchristos     z_stream strm;
4705ba6b03cSchristos 
4715ba6b03cSchristos     /* ignore command name */
47275f9f1baSchristos     argc--; argv++;
4735ba6b03cSchristos 
4745ba6b03cSchristos     /* provide usage if no arguments */
4755ba6b03cSchristos     if (*argv == NULL) {
47675f9f1baSchristos         printf(
47775f9f1baSchristos             "gzappend 1.2 (11 Oct 2012) Copyright (C) 2003, 2012 Mark Adler\n"
47875f9f1baSchristos                );
4795ba6b03cSchristos         printf(
4805ba6b03cSchristos             "usage: gzappend [-level] file.gz [ addthis [ andthis ... ]]\n");
4815ba6b03cSchristos         return 0;
4825ba6b03cSchristos     }
4835ba6b03cSchristos 
4845ba6b03cSchristos     /* set compression level */
4855ba6b03cSchristos     level = Z_DEFAULT_COMPRESSION;
4865ba6b03cSchristos     if (argv[0][0] == '-') {
4875ba6b03cSchristos         if (argv[0][1] < '0' || argv[0][1] > '9' || argv[0][2] != 0)
4885ba6b03cSchristos             bye("invalid compression level", "");
4895ba6b03cSchristos         level = argv[0][1] - '0';
4905ba6b03cSchristos         if (*++argv == NULL) bye("no gzip file name after options", "");
4915ba6b03cSchristos     }
4925ba6b03cSchristos 
4935ba6b03cSchristos     /* prepare to append to gzip file */
4945ba6b03cSchristos     gd = gzscan(*argv++, &strm, level);
4955ba6b03cSchristos 
4965ba6b03cSchristos     /* append files on command line, or from stdin if none */
4975ba6b03cSchristos     if (*argv == NULL)
4985ba6b03cSchristos         gztack(NULL, gd, &strm, 1);
4995ba6b03cSchristos     else
5005ba6b03cSchristos         do {
5015ba6b03cSchristos             gztack(*argv, gd, &strm, argv[1] == NULL);
5025ba6b03cSchristos         } while (*++argv != NULL);
5035ba6b03cSchristos     return 0;
5045ba6b03cSchristos }
505