1275793eaSopenharmony_ci/* gzappend -- command to append to a gzip file
2275793eaSopenharmony_ci
3275793eaSopenharmony_ci  Copyright (C) 2003, 2012 Mark Adler, all rights reserved
4275793eaSopenharmony_ci  version 1.2, 11 Oct 2012
5275793eaSopenharmony_ci
6275793eaSopenharmony_ci  This software is provided 'as-is', without any express or implied
7275793eaSopenharmony_ci  warranty.  In no event will the author be held liable for any damages
8275793eaSopenharmony_ci  arising from the use of this software.
9275793eaSopenharmony_ci
10275793eaSopenharmony_ci  Permission is granted to anyone to use this software for any purpose,
11275793eaSopenharmony_ci  including commercial applications, and to alter it and redistribute it
12275793eaSopenharmony_ci  freely, subject to the following restrictions:
13275793eaSopenharmony_ci
14275793eaSopenharmony_ci  1. The origin of this software must not be misrepresented; you must not
15275793eaSopenharmony_ci     claim that you wrote the original software. If you use this software
16275793eaSopenharmony_ci     in a product, an acknowledgment in the product documentation would be
17275793eaSopenharmony_ci     appreciated but is not required.
18275793eaSopenharmony_ci  2. Altered source versions must be plainly marked as such, and must not be
19275793eaSopenharmony_ci     misrepresented as being the original software.
20275793eaSopenharmony_ci  3. This notice may not be removed or altered from any source distribution.
21275793eaSopenharmony_ci
22275793eaSopenharmony_ci  Mark Adler    madler@alumni.caltech.edu
23275793eaSopenharmony_ci */
24275793eaSopenharmony_ci
25275793eaSopenharmony_ci/*
26275793eaSopenharmony_ci * Change history:
27275793eaSopenharmony_ci *
28275793eaSopenharmony_ci * 1.0  19 Oct 2003     - First version
29275793eaSopenharmony_ci * 1.1   4 Nov 2003     - Expand and clarify some comments and notes
30275793eaSopenharmony_ci *                      - Add version and copyright to help
31275793eaSopenharmony_ci *                      - Send help to stdout instead of stderr
32275793eaSopenharmony_ci *                      - Add some preemptive typecasts
33275793eaSopenharmony_ci *                      - Add L to constants in lseek() calls
34275793eaSopenharmony_ci *                      - Remove some debugging information in error messages
35275793eaSopenharmony_ci *                      - Use new data_type definition for zlib 1.2.1
36275793eaSopenharmony_ci *                      - Simplify and unify file operations
37275793eaSopenharmony_ci *                      - Finish off gzip file in gztack()
38275793eaSopenharmony_ci *                      - Use deflatePrime() instead of adding empty blocks
39275793eaSopenharmony_ci *                      - Keep gzip file clean on appended file read errors
40275793eaSopenharmony_ci *                      - Use in-place rotate instead of auxiliary buffer
41275793eaSopenharmony_ci *                        (Why you ask?  Because it was fun to write!)
42275793eaSopenharmony_ci * 1.2  11 Oct 2012     - Fix for proper z_const usage
43275793eaSopenharmony_ci *                      - Check for input buffer malloc failure
44275793eaSopenharmony_ci */
45275793eaSopenharmony_ci
46275793eaSopenharmony_ci/*
47275793eaSopenharmony_ci   gzappend takes a gzip file and appends to it, compressing files from the
48275793eaSopenharmony_ci   command line or data from stdin.  The gzip file is written to directly, to
49275793eaSopenharmony_ci   avoid copying that file, in case it's large.  Note that this results in the
50275793eaSopenharmony_ci   unfriendly behavior that if gzappend fails, the gzip file is corrupted.
51275793eaSopenharmony_ci
52275793eaSopenharmony_ci   This program was written to illustrate the use of the new Z_BLOCK option of
53275793eaSopenharmony_ci   zlib 1.2.x's inflate() function.  This option returns from inflate() at each
54275793eaSopenharmony_ci   block boundary to facilitate locating and modifying the last block bit at
55275793eaSopenharmony_ci   the start of the final deflate block.  Also whether using Z_BLOCK or not,
56275793eaSopenharmony_ci   another required feature of zlib 1.2.x is that inflate() now provides the
57275793eaSopenharmony_ci   number of unused bits in the last input byte used.  gzappend will not work
58275793eaSopenharmony_ci   with versions of zlib earlier than 1.2.1.
59275793eaSopenharmony_ci
60275793eaSopenharmony_ci   gzappend first decompresses the gzip file internally, discarding all but
61275793eaSopenharmony_ci   the last 32K of uncompressed data, and noting the location of the last block
62275793eaSopenharmony_ci   bit and the number of unused bits in the last byte of the compressed data.
63275793eaSopenharmony_ci   The gzip trailer containing the CRC-32 and length of the uncompressed data
64275793eaSopenharmony_ci   is verified.  This trailer will be later overwritten.
65275793eaSopenharmony_ci
66275793eaSopenharmony_ci   Then the last block bit is cleared by seeking back in the file and rewriting
67275793eaSopenharmony_ci   the byte that contains it.  Seeking forward, the last byte of the compressed
68275793eaSopenharmony_ci   data is saved along with the number of unused bits to initialize deflate.
69275793eaSopenharmony_ci
70275793eaSopenharmony_ci   A deflate process is initialized, using the last 32K of the uncompressed
71275793eaSopenharmony_ci   data from the gzip file to initialize the dictionary.  If the total
72275793eaSopenharmony_ci   uncompressed data was less than 32K, then all of it is used to initialize
73275793eaSopenharmony_ci   the dictionary.  The deflate output bit buffer is also initialized with the
74275793eaSopenharmony_ci   last bits from the original deflate stream.  From here on, the data to
75275793eaSopenharmony_ci   append is simply compressed using deflate, and written to the gzip file.
76275793eaSopenharmony_ci   When that is complete, the new CRC-32 and uncompressed length are written
77275793eaSopenharmony_ci   as the trailer of the gzip file.
78275793eaSopenharmony_ci */
79275793eaSopenharmony_ci
80275793eaSopenharmony_ci#include <stdio.h>
81275793eaSopenharmony_ci#include <stdlib.h>
82275793eaSopenharmony_ci#include <string.h>
83275793eaSopenharmony_ci#include <fcntl.h>
84275793eaSopenharmony_ci#include <unistd.h>
85275793eaSopenharmony_ci#include "zlib.h"
86275793eaSopenharmony_ci
87275793eaSopenharmony_ci#define local static
88275793eaSopenharmony_ci#define LGCHUNK 14
89275793eaSopenharmony_ci#define CHUNK (1U << LGCHUNK)
90275793eaSopenharmony_ci#define DSIZE 32768U
91275793eaSopenharmony_ci
92275793eaSopenharmony_ci/* print an error message and terminate with extreme prejudice */
93275793eaSopenharmony_cilocal void bye(char *msg1, char *msg2)
94275793eaSopenharmony_ci{
95275793eaSopenharmony_ci    fprintf(stderr, "gzappend error: %s%s\n", msg1, msg2);
96275793eaSopenharmony_ci    exit(1);
97275793eaSopenharmony_ci}
98275793eaSopenharmony_ci
99275793eaSopenharmony_ci/* return the greatest common divisor of a and b using Euclid's algorithm,
100275793eaSopenharmony_ci   modified to be fast when one argument much greater than the other, and
101275793eaSopenharmony_ci   coded to avoid unnecessary swapping */
102275793eaSopenharmony_cilocal unsigned gcd(unsigned a, unsigned b)
103275793eaSopenharmony_ci{
104275793eaSopenharmony_ci    unsigned c;
105275793eaSopenharmony_ci
106275793eaSopenharmony_ci    while (a && b)
107275793eaSopenharmony_ci        if (a > b) {
108275793eaSopenharmony_ci            c = b;
109275793eaSopenharmony_ci            while (a - c >= c)
110275793eaSopenharmony_ci                c <<= 1;
111275793eaSopenharmony_ci            a -= c;
112275793eaSopenharmony_ci        }
113275793eaSopenharmony_ci        else {
114275793eaSopenharmony_ci            c = a;
115275793eaSopenharmony_ci            while (b - c >= c)
116275793eaSopenharmony_ci                c <<= 1;
117275793eaSopenharmony_ci            b -= c;
118275793eaSopenharmony_ci        }
119275793eaSopenharmony_ci    return a + b;
120275793eaSopenharmony_ci}
121275793eaSopenharmony_ci
122275793eaSopenharmony_ci/* rotate list[0..len-1] left by rot positions, in place */
123275793eaSopenharmony_cilocal void rotate(unsigned char *list, unsigned len, unsigned rot)
124275793eaSopenharmony_ci{
125275793eaSopenharmony_ci    unsigned char tmp;
126275793eaSopenharmony_ci    unsigned cycles;
127275793eaSopenharmony_ci    unsigned char *start, *last, *to, *from;
128275793eaSopenharmony_ci
129275793eaSopenharmony_ci    /* normalize rot and handle degenerate cases */
130275793eaSopenharmony_ci    if (len < 2) return;
131275793eaSopenharmony_ci    if (rot >= len) rot %= len;
132275793eaSopenharmony_ci    if (rot == 0) return;
133275793eaSopenharmony_ci
134275793eaSopenharmony_ci    /* pointer to last entry in list */
135275793eaSopenharmony_ci    last = list + (len - 1);
136275793eaSopenharmony_ci
137275793eaSopenharmony_ci    /* do simple left shift by one */
138275793eaSopenharmony_ci    if (rot == 1) {
139275793eaSopenharmony_ci        tmp = *list;
140275793eaSopenharmony_ci        memmove(list, list + 1, len - 1);
141275793eaSopenharmony_ci        *last = tmp;
142275793eaSopenharmony_ci        return;
143275793eaSopenharmony_ci    }
144275793eaSopenharmony_ci
145275793eaSopenharmony_ci    /* do simple right shift by one */
146275793eaSopenharmony_ci    if (rot == len - 1) {
147275793eaSopenharmony_ci        tmp = *last;
148275793eaSopenharmony_ci        memmove(list + 1, list, len - 1);
149275793eaSopenharmony_ci        *list = tmp;
150275793eaSopenharmony_ci        return;
151275793eaSopenharmony_ci    }
152275793eaSopenharmony_ci
153275793eaSopenharmony_ci    /* otherwise do rotate as a set of cycles in place */
154275793eaSopenharmony_ci    cycles = gcd(len, rot);             /* number of cycles */
155275793eaSopenharmony_ci    do {
156275793eaSopenharmony_ci        start = from = list + cycles;   /* start index is arbitrary */
157275793eaSopenharmony_ci        tmp = *from;                    /* save entry to be overwritten */
158275793eaSopenharmony_ci        for (;;) {
159275793eaSopenharmony_ci            to = from;                  /* next step in cycle */
160275793eaSopenharmony_ci            from += rot;                /* go right rot positions */
161275793eaSopenharmony_ci            if (from > last) from -= len;   /* (pointer better not wrap) */
162275793eaSopenharmony_ci            if (from == start) break;   /* all but one shifted */
163275793eaSopenharmony_ci            *to = *from;                /* shift left */
164275793eaSopenharmony_ci        }
165275793eaSopenharmony_ci        *to = tmp;                      /* complete the circle */
166275793eaSopenharmony_ci    } while (--cycles);
167275793eaSopenharmony_ci}
168275793eaSopenharmony_ci
169275793eaSopenharmony_ci/* structure for gzip file read operations */
170275793eaSopenharmony_citypedef struct {
171275793eaSopenharmony_ci    int fd;                     /* file descriptor */
172275793eaSopenharmony_ci    int size;                   /* 1 << size is bytes in buf */
173275793eaSopenharmony_ci    unsigned left;              /* bytes available at next */
174275793eaSopenharmony_ci    unsigned char *buf;         /* buffer */
175275793eaSopenharmony_ci    z_const unsigned char *next;    /* next byte in buffer */
176275793eaSopenharmony_ci    char *name;                 /* file name for error messages */
177275793eaSopenharmony_ci} file;
178275793eaSopenharmony_ci
179275793eaSopenharmony_ci/* reload buffer */
180275793eaSopenharmony_cilocal int readin(file *in)
181275793eaSopenharmony_ci{
182275793eaSopenharmony_ci    int len;
183275793eaSopenharmony_ci
184275793eaSopenharmony_ci    len = read(in->fd, in->buf, 1 << in->size);
185275793eaSopenharmony_ci    if (len == -1) bye("error reading ", in->name);
186275793eaSopenharmony_ci    in->left = (unsigned)len;
187275793eaSopenharmony_ci    in->next = in->buf;
188275793eaSopenharmony_ci    return len;
189275793eaSopenharmony_ci}
190275793eaSopenharmony_ci
191275793eaSopenharmony_ci/* read from file in, exit if end-of-file */
192275793eaSopenharmony_cilocal int readmore(file *in)
193275793eaSopenharmony_ci{
194275793eaSopenharmony_ci    if (readin(in) == 0) bye("unexpected end of ", in->name);
195275793eaSopenharmony_ci    return 0;
196275793eaSopenharmony_ci}
197275793eaSopenharmony_ci
198275793eaSopenharmony_ci#define read1(in) (in->left == 0 ? readmore(in) : 0, \
199275793eaSopenharmony_ci                   in->left--, *(in->next)++)
200275793eaSopenharmony_ci
201275793eaSopenharmony_ci/* skip over n bytes of in */
202275793eaSopenharmony_cilocal void skip(file *in, unsigned n)
203275793eaSopenharmony_ci{
204275793eaSopenharmony_ci    unsigned bypass;
205275793eaSopenharmony_ci
206275793eaSopenharmony_ci    if (n > in->left) {
207275793eaSopenharmony_ci        n -= in->left;
208275793eaSopenharmony_ci        bypass = n & ~((1U << in->size) - 1);
209275793eaSopenharmony_ci        if (bypass) {
210275793eaSopenharmony_ci            if (lseek(in->fd, (off_t)bypass, SEEK_CUR) == -1)
211275793eaSopenharmony_ci                bye("seeking ", in->name);
212275793eaSopenharmony_ci            n -= bypass;
213275793eaSopenharmony_ci        }
214275793eaSopenharmony_ci        readmore(in);
215275793eaSopenharmony_ci        if (n > in->left)
216275793eaSopenharmony_ci            bye("unexpected end of ", in->name);
217275793eaSopenharmony_ci    }
218275793eaSopenharmony_ci    in->left -= n;
219275793eaSopenharmony_ci    in->next += n;
220275793eaSopenharmony_ci}
221275793eaSopenharmony_ci
222275793eaSopenharmony_ci/* read a four-byte unsigned integer, little-endian, from in */
223275793eaSopenharmony_ciunsigned long read4(file *in)
224275793eaSopenharmony_ci{
225275793eaSopenharmony_ci    unsigned long val;
226275793eaSopenharmony_ci
227275793eaSopenharmony_ci    val = read1(in);
228275793eaSopenharmony_ci    val += (unsigned)read1(in) << 8;
229275793eaSopenharmony_ci    val += (unsigned long)read1(in) << 16;
230275793eaSopenharmony_ci    val += (unsigned long)read1(in) << 24;
231275793eaSopenharmony_ci    return val;
232275793eaSopenharmony_ci}
233275793eaSopenharmony_ci
234275793eaSopenharmony_ci/* skip over gzip header */
235275793eaSopenharmony_cilocal void gzheader(file *in)
236275793eaSopenharmony_ci{
237275793eaSopenharmony_ci    int flags;
238275793eaSopenharmony_ci    unsigned n;
239275793eaSopenharmony_ci
240275793eaSopenharmony_ci    if (read1(in) != 31 || read1(in) != 139) bye(in->name, " not a gzip file");
241275793eaSopenharmony_ci    if (read1(in) != 8) bye("unknown compression method in", in->name);
242275793eaSopenharmony_ci    flags = read1(in);
243275793eaSopenharmony_ci    if (flags & 0xe0) bye("unknown header flags set in", in->name);
244275793eaSopenharmony_ci    skip(in, 6);
245275793eaSopenharmony_ci    if (flags & 4) {
246275793eaSopenharmony_ci        n = read1(in);
247275793eaSopenharmony_ci        n += (unsigned)(read1(in)) << 8;
248275793eaSopenharmony_ci        skip(in, n);
249275793eaSopenharmony_ci    }
250275793eaSopenharmony_ci    if (flags & 8) while (read1(in) != 0) ;
251275793eaSopenharmony_ci    if (flags & 16) while (read1(in) != 0) ;
252275793eaSopenharmony_ci    if (flags & 2) skip(in, 2);
253275793eaSopenharmony_ci}
254275793eaSopenharmony_ci
255275793eaSopenharmony_ci/* decompress gzip file "name", return strm with a deflate stream ready to
256275793eaSopenharmony_ci   continue compression of the data in the gzip file, and return a file
257275793eaSopenharmony_ci   descriptor pointing to where to write the compressed data -- the deflate
258275793eaSopenharmony_ci   stream is initialized to compress using level "level" */
259275793eaSopenharmony_cilocal int gzscan(char *name, z_stream *strm, int level)
260275793eaSopenharmony_ci{
261275793eaSopenharmony_ci    int ret, lastbit, left, full;
262275793eaSopenharmony_ci    unsigned have;
263275793eaSopenharmony_ci    unsigned long crc, tot;
264275793eaSopenharmony_ci    unsigned char *window;
265275793eaSopenharmony_ci    off_t lastoff, end;
266275793eaSopenharmony_ci    file gz;
267275793eaSopenharmony_ci
268275793eaSopenharmony_ci    /* open gzip file */
269275793eaSopenharmony_ci    gz.name = name;
270275793eaSopenharmony_ci    gz.fd = open(name, O_RDWR, 0);
271275793eaSopenharmony_ci    if (gz.fd == -1) bye("cannot open ", name);
272275793eaSopenharmony_ci    gz.buf = malloc(CHUNK);
273275793eaSopenharmony_ci    if (gz.buf == NULL) bye("out of memory", "");
274275793eaSopenharmony_ci    gz.size = LGCHUNK;
275275793eaSopenharmony_ci    gz.left = 0;
276275793eaSopenharmony_ci
277275793eaSopenharmony_ci    /* skip gzip header */
278275793eaSopenharmony_ci    gzheader(&gz);
279275793eaSopenharmony_ci
280275793eaSopenharmony_ci    /* prepare to decompress */
281275793eaSopenharmony_ci    window = malloc(DSIZE);
282275793eaSopenharmony_ci    if (window == NULL) bye("out of memory", "");
283275793eaSopenharmony_ci    strm->zalloc = Z_NULL;
284275793eaSopenharmony_ci    strm->zfree = Z_NULL;
285275793eaSopenharmony_ci    strm->opaque = Z_NULL;
286275793eaSopenharmony_ci    ret = inflateInit2(strm, -15);
287275793eaSopenharmony_ci    if (ret != Z_OK) bye("out of memory", " or library mismatch");
288275793eaSopenharmony_ci
289275793eaSopenharmony_ci    /* decompress the deflate stream, saving append information */
290275793eaSopenharmony_ci    lastbit = 0;
291275793eaSopenharmony_ci    lastoff = lseek(gz.fd, 0L, SEEK_CUR) - gz.left;
292275793eaSopenharmony_ci    left = 0;
293275793eaSopenharmony_ci    strm->avail_in = gz.left;
294275793eaSopenharmony_ci    strm->next_in = gz.next;
295275793eaSopenharmony_ci    crc = crc32(0L, Z_NULL, 0);
296275793eaSopenharmony_ci    have = full = 0;
297275793eaSopenharmony_ci    do {
298275793eaSopenharmony_ci        /* if needed, get more input */
299275793eaSopenharmony_ci        if (strm->avail_in == 0) {
300275793eaSopenharmony_ci            readmore(&gz);
301275793eaSopenharmony_ci            strm->avail_in = gz.left;
302275793eaSopenharmony_ci            strm->next_in = gz.next;
303275793eaSopenharmony_ci        }
304275793eaSopenharmony_ci
305275793eaSopenharmony_ci        /* set up output to next available section of sliding window */
306275793eaSopenharmony_ci        strm->avail_out = DSIZE - have;
307275793eaSopenharmony_ci        strm->next_out = window + have;
308275793eaSopenharmony_ci
309275793eaSopenharmony_ci        /* inflate and check for errors */
310275793eaSopenharmony_ci        ret = inflate(strm, Z_BLOCK);
311275793eaSopenharmony_ci        if (ret == Z_STREAM_ERROR) bye("internal stream error!", "");
312275793eaSopenharmony_ci        if (ret == Z_MEM_ERROR) bye("out of memory", "");
313275793eaSopenharmony_ci        if (ret == Z_DATA_ERROR)
314275793eaSopenharmony_ci            bye("invalid compressed data--format violated in", name);
315275793eaSopenharmony_ci
316275793eaSopenharmony_ci        /* update crc and sliding window pointer */
317275793eaSopenharmony_ci        crc = crc32(crc, window + have, DSIZE - have - strm->avail_out);
318275793eaSopenharmony_ci        if (strm->avail_out)
319275793eaSopenharmony_ci            have = DSIZE - strm->avail_out;
320275793eaSopenharmony_ci        else {
321275793eaSopenharmony_ci            have = 0;
322275793eaSopenharmony_ci            full = 1;
323275793eaSopenharmony_ci        }
324275793eaSopenharmony_ci
325275793eaSopenharmony_ci        /* process end of block */
326275793eaSopenharmony_ci        if (strm->data_type & 128) {
327275793eaSopenharmony_ci            if (strm->data_type & 64)
328275793eaSopenharmony_ci                left = strm->data_type & 0x1f;
329275793eaSopenharmony_ci            else {
330275793eaSopenharmony_ci                lastbit = strm->data_type & 0x1f;
331275793eaSopenharmony_ci                lastoff = lseek(gz.fd, 0L, SEEK_CUR) - strm->avail_in;
332275793eaSopenharmony_ci            }
333275793eaSopenharmony_ci        }
334275793eaSopenharmony_ci    } while (ret != Z_STREAM_END);
335275793eaSopenharmony_ci    inflateEnd(strm);
336275793eaSopenharmony_ci    gz.left = strm->avail_in;
337275793eaSopenharmony_ci    gz.next = strm->next_in;
338275793eaSopenharmony_ci
339275793eaSopenharmony_ci    /* save the location of the end of the compressed data */
340275793eaSopenharmony_ci    end = lseek(gz.fd, 0L, SEEK_CUR) - gz.left;
341275793eaSopenharmony_ci
342275793eaSopenharmony_ci    /* check gzip trailer and save total for deflate */
343275793eaSopenharmony_ci    if (crc != read4(&gz))
344275793eaSopenharmony_ci        bye("invalid compressed data--crc mismatch in ", name);
345275793eaSopenharmony_ci    tot = strm->total_out;
346275793eaSopenharmony_ci    if ((tot & 0xffffffffUL) != read4(&gz))
347275793eaSopenharmony_ci        bye("invalid compressed data--length mismatch in", name);
348275793eaSopenharmony_ci
349275793eaSopenharmony_ci    /* if not at end of file, warn */
350275793eaSopenharmony_ci    if (gz.left || readin(&gz))
351275793eaSopenharmony_ci        fprintf(stderr,
352275793eaSopenharmony_ci            "gzappend warning: junk at end of gzip file overwritten\n");
353275793eaSopenharmony_ci
354275793eaSopenharmony_ci    /* clear last block bit */
355275793eaSopenharmony_ci    lseek(gz.fd, lastoff - (lastbit != 0), SEEK_SET);
356275793eaSopenharmony_ci    if (read(gz.fd, gz.buf, 1) != 1) bye("reading after seek on ", name);
357275793eaSopenharmony_ci    *gz.buf = (unsigned char)(*gz.buf ^ (1 << ((8 - lastbit) & 7)));
358275793eaSopenharmony_ci    lseek(gz.fd, -1L, SEEK_CUR);
359275793eaSopenharmony_ci    if (write(gz.fd, gz.buf, 1) != 1) bye("writing after seek to ", name);
360275793eaSopenharmony_ci
361275793eaSopenharmony_ci    /* if window wrapped, build dictionary from window by rotating */
362275793eaSopenharmony_ci    if (full) {
363275793eaSopenharmony_ci        rotate(window, DSIZE, have);
364275793eaSopenharmony_ci        have = DSIZE;
365275793eaSopenharmony_ci    }
366275793eaSopenharmony_ci
367275793eaSopenharmony_ci    /* set up deflate stream with window, crc, total_in, and leftover bits */
368275793eaSopenharmony_ci    ret = deflateInit2(strm, level, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
369275793eaSopenharmony_ci    if (ret != Z_OK) bye("out of memory", "");
370275793eaSopenharmony_ci    deflateSetDictionary(strm, window, have);
371275793eaSopenharmony_ci    strm->adler = crc;
372275793eaSopenharmony_ci    strm->total_in = tot;
373275793eaSopenharmony_ci    if (left) {
374275793eaSopenharmony_ci        lseek(gz.fd, --end, SEEK_SET);
375275793eaSopenharmony_ci        if (read(gz.fd, gz.buf, 1) != 1) bye("reading after seek on ", name);
376275793eaSopenharmony_ci        deflatePrime(strm, 8 - left, *gz.buf);
377275793eaSopenharmony_ci    }
378275793eaSopenharmony_ci    lseek(gz.fd, end, SEEK_SET);
379275793eaSopenharmony_ci
380275793eaSopenharmony_ci    /* clean up and return */
381275793eaSopenharmony_ci    free(window);
382275793eaSopenharmony_ci    free(gz.buf);
383275793eaSopenharmony_ci    return gz.fd;
384275793eaSopenharmony_ci}
385275793eaSopenharmony_ci
386275793eaSopenharmony_ci/* append file "name" to gzip file gd using deflate stream strm -- if last
387275793eaSopenharmony_ci   is true, then finish off the deflate stream at the end */
388275793eaSopenharmony_cilocal void gztack(char *name, int gd, z_stream *strm, int last)
389275793eaSopenharmony_ci{
390275793eaSopenharmony_ci    int fd, len, ret;
391275793eaSopenharmony_ci    unsigned left;
392275793eaSopenharmony_ci    unsigned char *in, *out;
393275793eaSopenharmony_ci
394275793eaSopenharmony_ci    /* open file to compress and append */
395275793eaSopenharmony_ci    fd = 0;
396275793eaSopenharmony_ci    if (name != NULL) {
397275793eaSopenharmony_ci        fd = open(name, O_RDONLY, 0);
398275793eaSopenharmony_ci        if (fd == -1)
399275793eaSopenharmony_ci            fprintf(stderr, "gzappend warning: %s not found, skipping ...\n",
400275793eaSopenharmony_ci                    name);
401275793eaSopenharmony_ci    }
402275793eaSopenharmony_ci
403275793eaSopenharmony_ci    /* allocate buffers */
404275793eaSopenharmony_ci    in = malloc(CHUNK);
405275793eaSopenharmony_ci    out = malloc(CHUNK);
406275793eaSopenharmony_ci    if (in == NULL || out == NULL) bye("out of memory", "");
407275793eaSopenharmony_ci
408275793eaSopenharmony_ci    /* compress input file and append to gzip file */
409275793eaSopenharmony_ci    do {
410275793eaSopenharmony_ci        /* get more input */
411275793eaSopenharmony_ci        len = read(fd, in, CHUNK);
412275793eaSopenharmony_ci        if (len == -1) {
413275793eaSopenharmony_ci            fprintf(stderr,
414275793eaSopenharmony_ci                    "gzappend warning: error reading %s, skipping rest ...\n",
415275793eaSopenharmony_ci                    name);
416275793eaSopenharmony_ci            len = 0;
417275793eaSopenharmony_ci        }
418275793eaSopenharmony_ci        strm->avail_in = (unsigned)len;
419275793eaSopenharmony_ci        strm->next_in = in;
420275793eaSopenharmony_ci        if (len) strm->adler = crc32(strm->adler, in, (unsigned)len);
421275793eaSopenharmony_ci
422275793eaSopenharmony_ci        /* compress and write all available output */
423275793eaSopenharmony_ci        do {
424275793eaSopenharmony_ci            strm->avail_out = CHUNK;
425275793eaSopenharmony_ci            strm->next_out = out;
426275793eaSopenharmony_ci            ret = deflate(strm, last && len == 0 ? Z_FINISH : Z_NO_FLUSH);
427275793eaSopenharmony_ci            left = CHUNK - strm->avail_out;
428275793eaSopenharmony_ci            while (left) {
429275793eaSopenharmony_ci                len = write(gd, out + CHUNK - strm->avail_out - left, left);
430275793eaSopenharmony_ci                if (len == -1) bye("writing gzip file", "");
431275793eaSopenharmony_ci                left -= (unsigned)len;
432275793eaSopenharmony_ci            }
433275793eaSopenharmony_ci        } while (strm->avail_out == 0 && ret != Z_STREAM_END);
434275793eaSopenharmony_ci    } while (len != 0);
435275793eaSopenharmony_ci
436275793eaSopenharmony_ci    /* write trailer after last entry */
437275793eaSopenharmony_ci    if (last) {
438275793eaSopenharmony_ci        deflateEnd(strm);
439275793eaSopenharmony_ci        out[0] = (unsigned char)(strm->adler);
440275793eaSopenharmony_ci        out[1] = (unsigned char)(strm->adler >> 8);
441275793eaSopenharmony_ci        out[2] = (unsigned char)(strm->adler >> 16);
442275793eaSopenharmony_ci        out[3] = (unsigned char)(strm->adler >> 24);
443275793eaSopenharmony_ci        out[4] = (unsigned char)(strm->total_in);
444275793eaSopenharmony_ci        out[5] = (unsigned char)(strm->total_in >> 8);
445275793eaSopenharmony_ci        out[6] = (unsigned char)(strm->total_in >> 16);
446275793eaSopenharmony_ci        out[7] = (unsigned char)(strm->total_in >> 24);
447275793eaSopenharmony_ci        len = 8;
448275793eaSopenharmony_ci        do {
449275793eaSopenharmony_ci            ret = write(gd, out + 8 - len, len);
450275793eaSopenharmony_ci            if (ret == -1) bye("writing gzip file", "");
451275793eaSopenharmony_ci            len -= ret;
452275793eaSopenharmony_ci        } while (len);
453275793eaSopenharmony_ci        close(gd);
454275793eaSopenharmony_ci    }
455275793eaSopenharmony_ci
456275793eaSopenharmony_ci    /* clean up and return */
457275793eaSopenharmony_ci    free(out);
458275793eaSopenharmony_ci    free(in);
459275793eaSopenharmony_ci    if (fd > 0) close(fd);
460275793eaSopenharmony_ci}
461275793eaSopenharmony_ci
462275793eaSopenharmony_ci/* process the compression level option if present, scan the gzip file, and
463275793eaSopenharmony_ci   append the specified files, or append the data from stdin if no other file
464275793eaSopenharmony_ci   names are provided on the command line -- the gzip file must be writable
465275793eaSopenharmony_ci   and seekable */
466275793eaSopenharmony_ciint main(int argc, char **argv)
467275793eaSopenharmony_ci{
468275793eaSopenharmony_ci    int gd, level;
469275793eaSopenharmony_ci    z_stream strm;
470275793eaSopenharmony_ci
471275793eaSopenharmony_ci    /* ignore command name */
472275793eaSopenharmony_ci    argc--; argv++;
473275793eaSopenharmony_ci
474275793eaSopenharmony_ci    /* provide usage if no arguments */
475275793eaSopenharmony_ci    if (*argv == NULL) {
476275793eaSopenharmony_ci        printf(
477275793eaSopenharmony_ci            "gzappend 1.2 (11 Oct 2012) Copyright (C) 2003, 2012 Mark Adler\n"
478275793eaSopenharmony_ci               );
479275793eaSopenharmony_ci        printf(
480275793eaSopenharmony_ci            "usage: gzappend [-level] file.gz [ addthis [ andthis ... ]]\n");
481275793eaSopenharmony_ci        return 0;
482275793eaSopenharmony_ci    }
483275793eaSopenharmony_ci
484275793eaSopenharmony_ci    /* set compression level */
485275793eaSopenharmony_ci    level = Z_DEFAULT_COMPRESSION;
486275793eaSopenharmony_ci    if (argv[0][0] == '-') {
487275793eaSopenharmony_ci        if (argv[0][1] < '0' || argv[0][1] > '9' || argv[0][2] != 0)
488275793eaSopenharmony_ci            bye("invalid compression level", "");
489275793eaSopenharmony_ci        level = argv[0][1] - '0';
490275793eaSopenharmony_ci        if (*++argv == NULL) bye("no gzip file name after options", "");
491275793eaSopenharmony_ci    }
492275793eaSopenharmony_ci
493275793eaSopenharmony_ci    /* prepare to append to gzip file */
494275793eaSopenharmony_ci    gd = gzscan(*argv++, &strm, level);
495275793eaSopenharmony_ci
496275793eaSopenharmony_ci    /* append files on command line, or from stdin if none */
497275793eaSopenharmony_ci    if (*argv == NULL)
498275793eaSopenharmony_ci        gztack(NULL, gd, &strm, 1);
499275793eaSopenharmony_ci    else
500275793eaSopenharmony_ci        do {
501275793eaSopenharmony_ci            gztack(*argv, gd, &strm, argv[1] == NULL);
502275793eaSopenharmony_ci        } while (*++argv != NULL);
503275793eaSopenharmony_ci    return 0;
504275793eaSopenharmony_ci}
505