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