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