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