Home | History | Annotate | Line # | Download | only in examples
gzappend.c revision 1.1
      1  1.1  christos /* gzappend -- command to append to a gzip file
      2  1.1  christos 
      3  1.1  christos   Copyright (C) 2003 Mark Adler, all rights reserved
      4  1.1  christos   version 1.1, 4 Nov 2003
      5  1.1  christos 
      6  1.1  christos   This software is provided 'as-is', without any express or implied
      7  1.1  christos   warranty.  In no event will the author be held liable for any damages
      8  1.1  christos   arising from the use of this software.
      9  1.1  christos 
     10  1.1  christos   Permission is granted to anyone to use this software for any purpose,
     11  1.1  christos   including commercial applications, and to alter it and redistribute it
     12  1.1  christos   freely, subject to the following restrictions:
     13  1.1  christos 
     14  1.1  christos   1. The origin of this software must not be misrepresented; you must not
     15  1.1  christos      claim that you wrote the original software. If you use this software
     16  1.1  christos      in a product, an acknowledgment in the product documentation would be
     17  1.1  christos      appreciated but is not required.
     18  1.1  christos   2. Altered source versions must be plainly marked as such, and must not be
     19  1.1  christos      misrepresented as being the original software.
     20  1.1  christos   3. This notice may not be removed or altered from any source distribution.
     21  1.1  christos 
     22  1.1  christos   Mark Adler    madler (at) alumni.caltech.edu
     23  1.1  christos  */
     24  1.1  christos 
     25  1.1  christos /*
     26  1.1  christos  * Change history:
     27  1.1  christos  *
     28  1.1  christos  * 1.0  19 Oct 2003     - First version
     29  1.1  christos  * 1.1   4 Nov 2003     - Expand and clarify some comments and notes
     30  1.1  christos  *                      - Add version and copyright to help
     31  1.1  christos  *                      - Send help to stdout instead of stderr
     32  1.1  christos  *                      - Add some preemptive typecasts
     33  1.1  christos  *                      - Add L to constants in lseek() calls
     34  1.1  christos  *                      - Remove some debugging information in error messages
     35  1.1  christos  *                      - Use new data_type definition for zlib 1.2.1
     36  1.1  christos  *                      - Simplfy and unify file operations
     37  1.1  christos  *                      - Finish off gzip file in gztack()
     38  1.1  christos  *                      - Use deflatePrime() instead of adding empty blocks
     39  1.1  christos  *                      - Keep gzip file clean on appended file read errors
     40  1.1  christos  *                      - Use in-place rotate instead of auxiliary buffer
     41  1.1  christos  *                        (Why you ask?  Because it was fun to write!)
     42  1.1  christos  */
     43  1.1  christos 
     44  1.1  christos /*
     45  1.1  christos    gzappend takes a gzip file and appends to it, compressing files from the
     46  1.1  christos    command line or data from stdin.  The gzip file is written to directly, to
     47  1.1  christos    avoid copying that file, in case it's large.  Note that this results in the
     48  1.1  christos    unfriendly behavior that if gzappend fails, the gzip file is corrupted.
     49  1.1  christos 
     50  1.1  christos    This program was written to illustrate the use of the new Z_BLOCK option of
     51  1.1  christos    zlib 1.2.x's inflate() function.  This option returns from inflate() at each
     52  1.1  christos    block boundary to facilitate locating and modifying the last block bit at
     53  1.1  christos    the start of the final deflate block.  Also whether using Z_BLOCK or not,
     54  1.1  christos    another required feature of zlib 1.2.x is that inflate() now provides the
     55  1.1  christos    number of unusued bits in the last input byte used.  gzappend will not work
     56  1.1  christos    with versions of zlib earlier than 1.2.1.
     57  1.1  christos 
     58  1.1  christos    gzappend first decompresses the gzip file internally, discarding all but
     59  1.1  christos    the last 32K of uncompressed data, and noting the location of the last block
     60  1.1  christos    bit and the number of unused bits in the last byte of the compressed data.
     61  1.1  christos    The gzip trailer containing the CRC-32 and length of the uncompressed data
     62  1.1  christos    is verified.  This trailer will be later overwritten.
     63  1.1  christos 
     64  1.1  christos    Then the last block bit is cleared by seeking back in the file and rewriting
     65  1.1  christos    the byte that contains it.  Seeking forward, the last byte of the compressed
     66  1.1  christos    data is saved along with the number of unused bits to initialize deflate.
     67  1.1  christos 
     68  1.1  christos    A deflate process is initialized, using the last 32K of the uncompressed
     69  1.1  christos    data from the gzip file to initialize the dictionary.  If the total
     70  1.1  christos    uncompressed data was less than 32K, then all of it is used to initialize
     71  1.1  christos    the dictionary.  The deflate output bit buffer is also initialized with the
     72  1.1  christos    last bits from the original deflate stream.  From here on, the data to
     73  1.1  christos    append is simply compressed using deflate, and written to the gzip file.
     74  1.1  christos    When that is complete, the new CRC-32 and uncompressed length are written
     75  1.1  christos    as the trailer of the gzip file.
     76  1.1  christos  */
     77  1.1  christos 
     78  1.1  christos #include <stdio.h>
     79  1.1  christos #include <stdlib.h>
     80  1.1  christos #include <string.h>
     81  1.1  christos #include <fcntl.h>
     82  1.1  christos #include <unistd.h>
     83  1.1  christos #include "zlib.h"
     84  1.1  christos 
     85  1.1  christos #define local static
     86  1.1  christos #define LGCHUNK 14
     87  1.1  christos #define CHUNK (1U << LGCHUNK)
     88  1.1  christos #define DSIZE 32768U
     89  1.1  christos 
     90  1.1  christos /* print an error message and terminate with extreme prejudice */
     91  1.1  christos local void bye(char *msg1, char *msg2)
     92  1.1  christos {
     93  1.1  christos     fprintf(stderr, "gzappend error: %s%s\n", msg1, msg2);
     94  1.1  christos     exit(1);
     95  1.1  christos }
     96  1.1  christos 
     97  1.1  christos /* return the greatest common divisor of a and b using Euclid's algorithm,
     98  1.1  christos    modified to be fast when one argument much greater than the other, and
     99  1.1  christos    coded to avoid unnecessary swapping */
    100  1.1  christos local unsigned gcd(unsigned a, unsigned b)
    101  1.1  christos {
    102  1.1  christos     unsigned c;
    103  1.1  christos 
    104  1.1  christos     while (a && b)
    105  1.1  christos         if (a > b) {
    106  1.1  christos             c = b;
    107  1.1  christos             while (a - c >= c)
    108  1.1  christos                 c <<= 1;
    109  1.1  christos             a -= c;
    110  1.1  christos         }
    111  1.1  christos         else {
    112  1.1  christos             c = a;
    113  1.1  christos             while (b - c >= c)
    114  1.1  christos                 c <<= 1;
    115  1.1  christos             b -= c;
    116  1.1  christos         }
    117  1.1  christos     return a + b;
    118  1.1  christos }
    119  1.1  christos 
    120  1.1  christos /* rotate list[0..len-1] left by rot positions, in place */
    121  1.1  christos local void rotate(unsigned char *list, unsigned len, unsigned rot)
    122  1.1  christos {
    123  1.1  christos     unsigned char tmp;
    124  1.1  christos     unsigned cycles;
    125  1.1  christos     unsigned char *start, *last, *to, *from;
    126  1.1  christos 
    127  1.1  christos     /* normalize rot and handle degenerate cases */
    128  1.1  christos     if (len < 2) return;
    129  1.1  christos     if (rot >= len) rot %= len;
    130  1.1  christos     if (rot == 0) return;
    131  1.1  christos 
    132  1.1  christos     /* pointer to last entry in list */
    133  1.1  christos     last = list + (len - 1);
    134  1.1  christos 
    135  1.1  christos     /* do simple left shift by one */
    136  1.1  christos     if (rot == 1) {
    137  1.1  christos         tmp = *list;
    138  1.1  christos         memcpy(list, list + 1, len - 1);
    139  1.1  christos         *last = tmp;
    140  1.1  christos         return;
    141  1.1  christos     }
    142  1.1  christos 
    143  1.1  christos     /* do simple right shift by one */
    144  1.1  christos     if (rot == len - 1) {
    145  1.1  christos         tmp = *last;
    146  1.1  christos         memmove(list + 1, list, len - 1);
    147  1.1  christos         *list = tmp;
    148  1.1  christos         return;
    149  1.1  christos     }
    150  1.1  christos 
    151  1.1  christos     /* otherwise do rotate as a set of cycles in place */
    152  1.1  christos     cycles = gcd(len, rot);             /* number of cycles */
    153  1.1  christos     do {
    154  1.1  christos         start = from = list + cycles;   /* start index is arbitrary */
    155  1.1  christos         tmp = *from;                    /* save entry to be overwritten */
    156  1.1  christos         for (;;) {
    157  1.1  christos             to = from;                  /* next step in cycle */
    158  1.1  christos             from += rot;                /* go right rot positions */
    159  1.1  christos             if (from > last) from -= len;   /* (pointer better not wrap) */
    160  1.1  christos             if (from == start) break;   /* all but one shifted */
    161  1.1  christos             *to = *from;                /* shift left */
    162  1.1  christos         }
    163  1.1  christos         *to = tmp;                      /* complete the circle */
    164  1.1  christos     } while (--cycles);
    165  1.1  christos }
    166  1.1  christos 
    167  1.1  christos /* structure for gzip file read operations */
    168  1.1  christos typedef struct {
    169  1.1  christos     int fd;                     /* file descriptor */
    170  1.1  christos     int size;                   /* 1 << size is bytes in buf */
    171  1.1  christos     unsigned left;              /* bytes available at next */
    172  1.1  christos     unsigned char *buf;         /* buffer */
    173  1.1  christos     unsigned char *next;        /* next byte in buffer */
    174  1.1  christos     char *name;                 /* file name for error messages */
    175  1.1  christos } file;
    176  1.1  christos 
    177  1.1  christos /* reload buffer */
    178  1.1  christos local int readin(file *in)
    179  1.1  christos {
    180  1.1  christos     int len;
    181  1.1  christos 
    182  1.1  christos     len = read(in->fd, in->buf, 1 << in->size);
    183  1.1  christos     if (len == -1) bye("error reading ", in->name);
    184  1.1  christos     in->left = (unsigned)len;
    185  1.1  christos     in->next = in->buf;
    186  1.1  christos     return len;
    187  1.1  christos }
    188  1.1  christos 
    189  1.1  christos /* read from file in, exit if end-of-file */
    190  1.1  christos local int readmore(file *in)
    191  1.1  christos {
    192  1.1  christos     if (readin(in) == 0) bye("unexpected end of ", in->name);
    193  1.1  christos     return 0;
    194  1.1  christos }
    195  1.1  christos 
    196  1.1  christos #define read1(in) (in->left == 0 ? readmore(in) : 0, \
    197  1.1  christos                    in->left--, *(in->next)++)
    198  1.1  christos 
    199  1.1  christos /* skip over n bytes of in */
    200  1.1  christos local void skip(file *in, unsigned n)
    201  1.1  christos {
    202  1.1  christos     unsigned bypass;
    203  1.1  christos 
    204  1.1  christos     if (n > in->left) {
    205  1.1  christos         n -= in->left;
    206  1.1  christos         bypass = n & ~((1U << in->size) - 1);
    207  1.1  christos         if (bypass) {
    208  1.1  christos             if (lseek(in->fd, (off_t)bypass, SEEK_CUR) == -1)
    209  1.1  christos                 bye("seeking ", in->name);
    210  1.1  christos             n -= bypass;
    211  1.1  christos         }
    212  1.1  christos         readmore(in);
    213  1.1  christos         if (n > in->left)
    214  1.1  christos             bye("unexpected end of ", in->name);
    215  1.1  christos     }
    216  1.1  christos     in->left -= n;
    217  1.1  christos     in->next += n;
    218  1.1  christos }
    219  1.1  christos 
    220  1.1  christos /* read a four-byte unsigned integer, little-endian, from in */
    221  1.1  christos unsigned long read4(file *in)
    222  1.1  christos {
    223  1.1  christos     unsigned long val;
    224  1.1  christos 
    225  1.1  christos     val = read1(in);
    226  1.1  christos     val += (unsigned)read1(in) << 8;
    227  1.1  christos     val += (unsigned long)read1(in) << 16;
    228  1.1  christos     val += (unsigned long)read1(in) << 24;
    229  1.1  christos     return val;
    230  1.1  christos }
    231  1.1  christos 
    232  1.1  christos /* skip over gzip header */
    233  1.1  christos local void gzheader(file *in)
    234  1.1  christos {
    235  1.1  christos     int flags;
    236  1.1  christos     unsigned n;
    237  1.1  christos 
    238  1.1  christos     if (read1(in) != 31 || read1(in) != 139) bye(in->name, " not a gzip file");
    239  1.1  christos     if (read1(in) != 8) bye("unknown compression method in", in->name);
    240  1.1  christos     flags = read1(in);
    241  1.1  christos     if (flags & 0xe0) bye("unknown header flags set in", in->name);
    242  1.1  christos     skip(in, 6);
    243  1.1  christos     if (flags & 4) {
    244  1.1  christos         n = read1(in);
    245  1.1  christos         n += (unsigned)(read1(in)) << 8;
    246  1.1  christos         skip(in, n);
    247  1.1  christos     }
    248  1.1  christos     if (flags & 8) while (read1(in) != 0) ;
    249  1.1  christos     if (flags & 16) while (read1(in) != 0) ;
    250  1.1  christos     if (flags & 2) skip(in, 2);
    251  1.1  christos }
    252  1.1  christos 
    253  1.1  christos /* decompress gzip file "name", return strm with a deflate stream ready to
    254  1.1  christos    continue compression of the data in the gzip file, and return a file
    255  1.1  christos    descriptor pointing to where to write the compressed data -- the deflate
    256  1.1  christos    stream is initialized to compress using level "level" */
    257  1.1  christos local int gzscan(char *name, z_stream *strm, int level)
    258  1.1  christos {
    259  1.1  christos     int ret, lastbit, left, full;
    260  1.1  christos     unsigned have;
    261  1.1  christos     unsigned long crc, tot;
    262  1.1  christos     unsigned char *window;
    263  1.1  christos     off_t lastoff, end;
    264  1.1  christos     file gz;
    265  1.1  christos 
    266  1.1  christos     /* open gzip file */
    267  1.1  christos     gz.name = name;
    268  1.1  christos     gz.fd = open(name, O_RDWR, 0);
    269  1.1  christos     if (gz.fd == -1) bye("cannot open ", name);
    270  1.1  christos     gz.buf = malloc(CHUNK);
    271  1.1  christos     if (gz.buf == NULL) bye("out of memory", "");
    272  1.1  christos     gz.size = LGCHUNK;
    273  1.1  christos     gz.left = 0;
    274  1.1  christos 
    275  1.1  christos     /* skip gzip header */
    276  1.1  christos     gzheader(&gz);
    277  1.1  christos 
    278  1.1  christos     /* prepare to decompress */
    279  1.1  christos     window = malloc(DSIZE);
    280  1.1  christos     if (window == NULL) bye("out of memory", "");
    281  1.1  christos     strm->zalloc = Z_NULL;
    282  1.1  christos     strm->zfree = Z_NULL;
    283  1.1  christos     strm->opaque = Z_NULL;
    284  1.1  christos     ret = inflateInit2(strm, -15);
    285  1.1  christos     if (ret != Z_OK) bye("out of memory", " or library mismatch");
    286  1.1  christos 
    287  1.1  christos     /* decompress the deflate stream, saving append information */
    288  1.1  christos     lastbit = 0;
    289  1.1  christos     lastoff = lseek(gz.fd, 0L, SEEK_CUR) - gz.left;
    290  1.1  christos     left = 0;
    291  1.1  christos     strm->avail_in = gz.left;
    292  1.1  christos     strm->next_in = gz.next;
    293  1.1  christos     crc = crc32(0L, Z_NULL, 0);
    294  1.1  christos     have = full = 0;
    295  1.1  christos     do {
    296  1.1  christos         /* if needed, get more input */
    297  1.1  christos         if (strm->avail_in == 0) {
    298  1.1  christos             readmore(&gz);
    299  1.1  christos             strm->avail_in = gz.left;
    300  1.1  christos             strm->next_in = gz.next;
    301  1.1  christos         }
    302  1.1  christos 
    303  1.1  christos         /* set up output to next available section of sliding window */
    304  1.1  christos         strm->avail_out = DSIZE - have;
    305  1.1  christos         strm->next_out = window + have;
    306  1.1  christos 
    307  1.1  christos         /* inflate and check for errors */
    308  1.1  christos         ret = inflate(strm, Z_BLOCK);
    309  1.1  christos         if (ret == Z_STREAM_ERROR) bye("internal stream error!", "");
    310  1.1  christos         if (ret == Z_MEM_ERROR) bye("out of memory", "");
    311  1.1  christos         if (ret == Z_DATA_ERROR)
    312  1.1  christos             bye("invalid compressed data--format violated in", name);
    313  1.1  christos 
    314  1.1  christos         /* update crc and sliding window pointer */
    315  1.1  christos         crc = crc32(crc, window + have, DSIZE - have - strm->avail_out);
    316  1.1  christos         if (strm->avail_out)
    317  1.1  christos             have = DSIZE - strm->avail_out;
    318  1.1  christos         else {
    319  1.1  christos             have = 0;
    320  1.1  christos             full = 1;
    321  1.1  christos         }
    322  1.1  christos 
    323  1.1  christos         /* process end of block */
    324  1.1  christos         if (strm->data_type & 128) {
    325  1.1  christos             if (strm->data_type & 64)
    326  1.1  christos                 left = strm->data_type & 0x1f;
    327  1.1  christos             else {
    328  1.1  christos                 lastbit = strm->data_type & 0x1f;
    329  1.1  christos                 lastoff = lseek(gz.fd, 0L, SEEK_CUR) - strm->avail_in;
    330  1.1  christos             }
    331  1.1  christos         }
    332  1.1  christos     } while (ret != Z_STREAM_END);
    333  1.1  christos     inflateEnd(strm);
    334  1.1  christos     gz.left = strm->avail_in;
    335  1.1  christos     gz.next = strm->next_in;
    336  1.1  christos 
    337  1.1  christos     /* save the location of the end of the compressed data */
    338  1.1  christos     end = lseek(gz.fd, 0L, SEEK_CUR) - gz.left;
    339  1.1  christos 
    340  1.1  christos     /* check gzip trailer and save total for deflate */
    341  1.1  christos     if (crc != read4(&gz))
    342  1.1  christos         bye("invalid compressed data--crc mismatch in ", name);
    343  1.1  christos     tot = strm->total_out;
    344  1.1  christos     if ((tot & 0xffffffffUL) != read4(&gz))
    345  1.1  christos         bye("invalid compressed data--length mismatch in", name);
    346  1.1  christos 
    347  1.1  christos     /* if not at end of file, warn */
    348  1.1  christos     if (gz.left || readin(&gz))
    349  1.1  christos         fprintf(stderr,
    350  1.1  christos             "gzappend warning: junk at end of gzip file overwritten\n");
    351  1.1  christos 
    352  1.1  christos     /* clear last block bit */
    353  1.1  christos     lseek(gz.fd, lastoff - (lastbit != 0), SEEK_SET);
    354  1.1  christos     if (read(gz.fd, gz.buf, 1) != 1) bye("reading after seek on ", name);
    355  1.1  christos     *gz.buf = (unsigned char)(*gz.buf ^ (1 << ((8 - lastbit) & 7)));
    356  1.1  christos     lseek(gz.fd, -1L, SEEK_CUR);
    357  1.1  christos     if (write(gz.fd, gz.buf, 1) != 1) bye("writing after seek to ", name);
    358  1.1  christos 
    359  1.1  christos     /* if window wrapped, build dictionary from window by rotating */
    360  1.1  christos     if (full) {
    361  1.1  christos         rotate(window, DSIZE, have);
    362  1.1  christos         have = DSIZE;
    363  1.1  christos     }
    364  1.1  christos 
    365  1.1  christos     /* set up deflate stream with window, crc, total_in, and leftover bits */
    366  1.1  christos     ret = deflateInit2(strm, level, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
    367  1.1  christos     if (ret != Z_OK) bye("out of memory", "");
    368  1.1  christos     deflateSetDictionary(strm, window, have);
    369  1.1  christos     strm->adler = crc;
    370  1.1  christos     strm->total_in = tot;
    371  1.1  christos     if (left) {
    372  1.1  christos         lseek(gz.fd, --end, SEEK_SET);
    373  1.1  christos         if (read(gz.fd, gz.buf, 1) != 1) bye("reading after seek on ", name);
    374  1.1  christos         deflatePrime(strm, 8 - left, *gz.buf);
    375  1.1  christos     }
    376  1.1  christos     lseek(gz.fd, end, SEEK_SET);
    377  1.1  christos 
    378  1.1  christos     /* clean up and return */
    379  1.1  christos     free(window);
    380  1.1  christos     free(gz.buf);
    381  1.1  christos     return gz.fd;
    382  1.1  christos }
    383  1.1  christos 
    384  1.1  christos /* append file "name" to gzip file gd using deflate stream strm -- if last
    385  1.1  christos    is true, then finish off the deflate stream at the end */
    386  1.1  christos local void gztack(char *name, int gd, z_stream *strm, int last)
    387  1.1  christos {
    388  1.1  christos     int fd, len, ret;
    389  1.1  christos     unsigned left;
    390  1.1  christos     unsigned char *in, *out;
    391  1.1  christos 
    392  1.1  christos     /* open file to compress and append */
    393  1.1  christos     fd = 0;
    394  1.1  christos     if (name != NULL) {
    395  1.1  christos         fd = open(name, O_RDONLY, 0);
    396  1.1  christos         if (fd == -1)
    397  1.1  christos             fprintf(stderr, "gzappend warning: %s not found, skipping ...\n",
    398  1.1  christos                     name);
    399  1.1  christos     }
    400  1.1  christos 
    401  1.1  christos     /* allocate buffers */
    402  1.1  christos     in = fd == -1 ? NULL : malloc(CHUNK);
    403  1.1  christos     out = malloc(CHUNK);
    404  1.1  christos     if (out == NULL) bye("out of memory", "");
    405  1.1  christos 
    406  1.1  christos     /* compress input file and append to gzip file */
    407  1.1  christos     do {
    408  1.1  christos         /* get more input */
    409  1.1  christos         len = fd == -1 ? 0 : read(fd, in, CHUNK);
    410  1.1  christos         if (len == -1) {
    411  1.1  christos             fprintf(stderr,
    412  1.1  christos                     "gzappend warning: error reading %s, skipping rest ...\n",
    413  1.1  christos                     name);
    414  1.1  christos             len = 0;
    415  1.1  christos         }
    416  1.1  christos         strm->avail_in = (unsigned)len;
    417  1.1  christos         strm->next_in = in;
    418  1.1  christos         if (len) strm->adler = crc32(strm->adler, in, (unsigned)len);
    419  1.1  christos 
    420  1.1  christos         /* compress and write all available output */
    421  1.1  christos         do {
    422  1.1  christos             strm->avail_out = CHUNK;
    423  1.1  christos             strm->next_out = out;
    424  1.1  christos             ret = deflate(strm, last && len == 0 ? Z_FINISH : Z_NO_FLUSH);
    425  1.1  christos             left = CHUNK - strm->avail_out;
    426  1.1  christos             while (left) {
    427  1.1  christos                 len = write(gd, out + CHUNK - strm->avail_out - left, left);
    428  1.1  christos                 if (len == -1) bye("writing gzip file", "");
    429  1.1  christos                 left -= (unsigned)len;
    430  1.1  christos             }
    431  1.1  christos         } while (strm->avail_out == 0 && ret != Z_STREAM_END);
    432  1.1  christos     } while (len != 0);
    433  1.1  christos 
    434  1.1  christos     /* write trailer after last entry */
    435  1.1  christos     if (last) {
    436  1.1  christos         deflateEnd(strm);
    437  1.1  christos         out[0] = (unsigned char)(strm->adler);
    438  1.1  christos         out[1] = (unsigned char)(strm->adler >> 8);
    439  1.1  christos         out[2] = (unsigned char)(strm->adler >> 16);
    440  1.1  christos         out[3] = (unsigned char)(strm->adler >> 24);
    441  1.1  christos         out[4] = (unsigned char)(strm->total_in);
    442  1.1  christos         out[5] = (unsigned char)(strm->total_in >> 8);
    443  1.1  christos         out[6] = (unsigned char)(strm->total_in >> 16);
    444  1.1  christos         out[7] = (unsigned char)(strm->total_in >> 24);
    445  1.1  christos         len = 8;
    446  1.1  christos         do {
    447  1.1  christos             ret = write(gd, out + 8 - len, len);
    448  1.1  christos             if (ret == -1) bye("writing gzip file", "");
    449  1.1  christos             len -= ret;
    450  1.1  christos         } while (len);
    451  1.1  christos         close(gd);
    452  1.1  christos     }
    453  1.1  christos 
    454  1.1  christos     /* clean up and return */
    455  1.1  christos     free(out);
    456  1.1  christos     if (in != NULL) free(in);
    457  1.1  christos     if (fd > 0) close(fd);
    458  1.1  christos }
    459  1.1  christos 
    460  1.1  christos /* process the compression level option if present, scan the gzip file, and
    461  1.1  christos    append the specified files, or append the data from stdin if no other file
    462  1.1  christos    names are provided on the command line -- the gzip file must be writable
    463  1.1  christos    and seekable */
    464  1.1  christos int main(int argc, char **argv)
    465  1.1  christos {
    466  1.1  christos     int gd, level;
    467  1.1  christos     z_stream strm;
    468  1.1  christos 
    469  1.1  christos     /* ignore command name */
    470  1.1  christos     argv++;
    471  1.1  christos 
    472  1.1  christos     /* provide usage if no arguments */
    473  1.1  christos     if (*argv == NULL) {
    474  1.1  christos         printf("gzappend 1.1 (4 Nov 2003) Copyright (C) 2003 Mark Adler\n");
    475  1.1  christos         printf(
    476  1.1  christos             "usage: gzappend [-level] file.gz [ addthis [ andthis ... ]]\n");
    477  1.1  christos         return 0;
    478  1.1  christos     }
    479  1.1  christos 
    480  1.1  christos     /* set compression level */
    481  1.1  christos     level = Z_DEFAULT_COMPRESSION;
    482  1.1  christos     if (argv[0][0] == '-') {
    483  1.1  christos         if (argv[0][1] < '0' || argv[0][1] > '9' || argv[0][2] != 0)
    484  1.1  christos             bye("invalid compression level", "");
    485  1.1  christos         level = argv[0][1] - '0';
    486  1.1  christos         if (*++argv == NULL) bye("no gzip file name after options", "");
    487  1.1  christos     }
    488  1.1  christos 
    489  1.1  christos     /* prepare to append to gzip file */
    490  1.1  christos     gd = gzscan(*argv++, &strm, level);
    491  1.1  christos 
    492  1.1  christos     /* append files on command line, or from stdin if none */
    493  1.1  christos     if (*argv == NULL)
    494  1.1  christos         gztack(NULL, gd, &strm, 1);
    495  1.1  christos     else
    496  1.1  christos         do {
    497  1.1  christos             gztack(*argv, gd, &strm, argv[1] == NULL);
    498  1.1  christos         } while (*++argv != NULL);
    499  1.1  christos     return 0;
    500  1.1  christos }
    501