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