xref: /netbsd-src/external/gpl3/gdb/dist/zlib/examples/gzappend.c (revision 4b169a6ba595ae283ca507b26b15fdff40495b1c)
1212397c6Schristos /* gzappend -- command to append to a gzip file
2212397c6Schristos 
3ba340e45Schristos   Copyright (C) 2003, 2012 Mark Adler, all rights reserved
4ba340e45Schristos   version 1.2, 11 Oct 2012
5212397c6Schristos 
6212397c6Schristos   This software is provided 'as-is', without any express or implied
7212397c6Schristos   warranty.  In no event will the author be held liable for any damages
8212397c6Schristos   arising from the use of this software.
9212397c6Schristos 
10212397c6Schristos   Permission is granted to anyone to use this software for any purpose,
11212397c6Schristos   including commercial applications, and to alter it and redistribute it
12212397c6Schristos   freely, subject to the following restrictions:
13212397c6Schristos 
14212397c6Schristos   1. The origin of this software must not be misrepresented; you must not
15212397c6Schristos      claim that you wrote the original software. If you use this software
16212397c6Schristos      in a product, an acknowledgment in the product documentation would be
17212397c6Schristos      appreciated but is not required.
18212397c6Schristos   2. Altered source versions must be plainly marked as such, and must not be
19212397c6Schristos      misrepresented as being the original software.
20212397c6Schristos   3. This notice may not be removed or altered from any source distribution.
21212397c6Schristos 
22212397c6Schristos   Mark Adler    madler@alumni.caltech.edu
23212397c6Schristos  */
24212397c6Schristos 
25212397c6Schristos /*
26212397c6Schristos  * Change history:
27212397c6Schristos  *
28212397c6Schristos  * 1.0  19 Oct 2003     - First version
29212397c6Schristos  * 1.1   4 Nov 2003     - Expand and clarify some comments and notes
30212397c6Schristos  *                      - Add version and copyright to help
31212397c6Schristos  *                      - Send help to stdout instead of stderr
32212397c6Schristos  *                      - Add some preemptive typecasts
33212397c6Schristos  *                      - Add L to constants in lseek() calls
34212397c6Schristos  *                      - Remove some debugging information in error messages
35212397c6Schristos  *                      - Use new data_type definition for zlib 1.2.1
36212397c6Schristos  *                      - Simplfy and unify file operations
37212397c6Schristos  *                      - Finish off gzip file in gztack()
38212397c6Schristos  *                      - Use deflatePrime() instead of adding empty blocks
39212397c6Schristos  *                      - Keep gzip file clean on appended file read errors
40212397c6Schristos  *                      - Use in-place rotate instead of auxiliary buffer
41212397c6Schristos  *                        (Why you ask?  Because it was fun to write!)
42ba340e45Schristos  * 1.2  11 Oct 2012     - Fix for proper z_const usage
43ba340e45Schristos  *                      - Check for input buffer malloc failure
44212397c6Schristos  */
45212397c6Schristos 
46212397c6Schristos /*
47212397c6Schristos    gzappend takes a gzip file and appends to it, compressing files from the
48212397c6Schristos    command line or data from stdin.  The gzip file is written to directly, to
49212397c6Schristos    avoid copying that file, in case it's large.  Note that this results in the
50212397c6Schristos    unfriendly behavior that if gzappend fails, the gzip file is corrupted.
51212397c6Schristos 
52212397c6Schristos    This program was written to illustrate the use of the new Z_BLOCK option of
53212397c6Schristos    zlib 1.2.x's inflate() function.  This option returns from inflate() at each
54212397c6Schristos    block boundary to facilitate locating and modifying the last block bit at
55212397c6Schristos    the start of the final deflate block.  Also whether using Z_BLOCK or not,
56212397c6Schristos    another required feature of zlib 1.2.x is that inflate() now provides the
57212397c6Schristos    number of unusued bits in the last input byte used.  gzappend will not work
58212397c6Schristos    with versions of zlib earlier than 1.2.1.
59212397c6Schristos 
60212397c6Schristos    gzappend first decompresses the gzip file internally, discarding all but
61212397c6Schristos    the last 32K of uncompressed data, and noting the location of the last block
62212397c6Schristos    bit and the number of unused bits in the last byte of the compressed data.
63212397c6Schristos    The gzip trailer containing the CRC-32 and length of the uncompressed data
64212397c6Schristos    is verified.  This trailer will be later overwritten.
65212397c6Schristos 
66212397c6Schristos    Then the last block bit is cleared by seeking back in the file and rewriting
67212397c6Schristos    the byte that contains it.  Seeking forward, the last byte of the compressed
68212397c6Schristos    data is saved along with the number of unused bits to initialize deflate.
69212397c6Schristos 
70212397c6Schristos    A deflate process is initialized, using the last 32K of the uncompressed
71212397c6Schristos    data from the gzip file to initialize the dictionary.  If the total
72212397c6Schristos    uncompressed data was less than 32K, then all of it is used to initialize
73212397c6Schristos    the dictionary.  The deflate output bit buffer is also initialized with the
74212397c6Schristos    last bits from the original deflate stream.  From here on, the data to
75212397c6Schristos    append is simply compressed using deflate, and written to the gzip file.
76212397c6Schristos    When that is complete, the new CRC-32 and uncompressed length are written
77212397c6Schristos    as the trailer of the gzip file.
78212397c6Schristos  */
79212397c6Schristos 
80212397c6Schristos #include <stdio.h>
81212397c6Schristos #include <stdlib.h>
82212397c6Schristos #include <string.h>
83212397c6Schristos #include <fcntl.h>
84212397c6Schristos #include <unistd.h>
85212397c6Schristos #include "zlib.h"
86212397c6Schristos 
87212397c6Schristos #define local static
88212397c6Schristos #define LGCHUNK 14
89212397c6Schristos #define CHUNK (1U << LGCHUNK)
90212397c6Schristos #define DSIZE 32768U
91212397c6Schristos 
92212397c6Schristos /* print an error message and terminate with extreme prejudice */
bye(char * msg1,char * msg2)93212397c6Schristos local void bye(char *msg1, char *msg2)
94212397c6Schristos {
95212397c6Schristos     fprintf(stderr, "gzappend error: %s%s\n", msg1, msg2);
96212397c6Schristos     exit(1);
97212397c6Schristos }
98212397c6Schristos 
99212397c6Schristos /* return the greatest common divisor of a and b using Euclid's algorithm,
100212397c6Schristos    modified to be fast when one argument much greater than the other, and
101212397c6Schristos    coded to avoid unnecessary swapping */
gcd(unsigned a,unsigned b)102212397c6Schristos local unsigned gcd(unsigned a, unsigned b)
103212397c6Schristos {
104212397c6Schristos     unsigned c;
105212397c6Schristos 
106212397c6Schristos     while (a && b)
107212397c6Schristos         if (a > b) {
108212397c6Schristos             c = b;
109212397c6Schristos             while (a - c >= c)
110212397c6Schristos                 c <<= 1;
111212397c6Schristos             a -= c;
112212397c6Schristos         }
113212397c6Schristos         else {
114212397c6Schristos             c = a;
115212397c6Schristos             while (b - c >= c)
116212397c6Schristos                 c <<= 1;
117212397c6Schristos             b -= c;
118212397c6Schristos         }
119212397c6Schristos     return a + b;
120212397c6Schristos }
121212397c6Schristos 
122212397c6Schristos /* rotate list[0..len-1] left by rot positions, in place */
rotate(unsigned char * list,unsigned len,unsigned rot)123212397c6Schristos local void rotate(unsigned char *list, unsigned len, unsigned rot)
124212397c6Schristos {
125212397c6Schristos     unsigned char tmp;
126212397c6Schristos     unsigned cycles;
127212397c6Schristos     unsigned char *start, *last, *to, *from;
128212397c6Schristos 
129212397c6Schristos     /* normalize rot and handle degenerate cases */
130212397c6Schristos     if (len < 2) return;
131212397c6Schristos     if (rot >= len) rot %= len;
132212397c6Schristos     if (rot == 0) return;
133212397c6Schristos 
134212397c6Schristos     /* pointer to last entry in list */
135212397c6Schristos     last = list + (len - 1);
136212397c6Schristos 
137212397c6Schristos     /* do simple left shift by one */
138212397c6Schristos     if (rot == 1) {
139212397c6Schristos         tmp = *list;
140*4b169a6bSchristos         memmove(list, list + 1, len - 1);
141212397c6Schristos         *last = tmp;
142212397c6Schristos         return;
143212397c6Schristos     }
144212397c6Schristos 
145212397c6Schristos     /* do simple right shift by one */
146212397c6Schristos     if (rot == len - 1) {
147212397c6Schristos         tmp = *last;
148212397c6Schristos         memmove(list + 1, list, len - 1);
149212397c6Schristos         *list = tmp;
150212397c6Schristos         return;
151212397c6Schristos     }
152212397c6Schristos 
153212397c6Schristos     /* otherwise do rotate as a set of cycles in place */
154212397c6Schristos     cycles = gcd(len, rot);             /* number of cycles */
155212397c6Schristos     do {
156212397c6Schristos         start = from = list + cycles;   /* start index is arbitrary */
157212397c6Schristos         tmp = *from;                    /* save entry to be overwritten */
158212397c6Schristos         for (;;) {
159212397c6Schristos             to = from;                  /* next step in cycle */
160212397c6Schristos             from += rot;                /* go right rot positions */
161212397c6Schristos             if (from > last) from -= len;   /* (pointer better not wrap) */
162212397c6Schristos             if (from == start) break;   /* all but one shifted */
163212397c6Schristos             *to = *from;                /* shift left */
164212397c6Schristos         }
165212397c6Schristos         *to = tmp;                      /* complete the circle */
166212397c6Schristos     } while (--cycles);
167212397c6Schristos }
168212397c6Schristos 
169212397c6Schristos /* structure for gzip file read operations */
170212397c6Schristos typedef struct {
171212397c6Schristos     int fd;                     /* file descriptor */
172212397c6Schristos     int size;                   /* 1 << size is bytes in buf */
173212397c6Schristos     unsigned left;              /* bytes available at next */
174212397c6Schristos     unsigned char *buf;         /* buffer */
175ba340e45Schristos     z_const unsigned char *next;    /* next byte in buffer */
176212397c6Schristos     char *name;                 /* file name for error messages */
177212397c6Schristos } file;
178212397c6Schristos 
179212397c6Schristos /* reload buffer */
readin(file * in)180212397c6Schristos local int readin(file *in)
181212397c6Schristos {
182212397c6Schristos     int len;
183212397c6Schristos 
184212397c6Schristos     len = read(in->fd, in->buf, 1 << in->size);
185212397c6Schristos     if (len == -1) bye("error reading ", in->name);
186212397c6Schristos     in->left = (unsigned)len;
187212397c6Schristos     in->next = in->buf;
188212397c6Schristos     return len;
189212397c6Schristos }
190212397c6Schristos 
191212397c6Schristos /* read from file in, exit if end-of-file */
readmore(file * in)192212397c6Schristos local int readmore(file *in)
193212397c6Schristos {
194212397c6Schristos     if (readin(in) == 0) bye("unexpected end of ", in->name);
195212397c6Schristos     return 0;
196212397c6Schristos }
197212397c6Schristos 
198212397c6Schristos #define read1(in) (in->left == 0 ? readmore(in) : 0, \
199212397c6Schristos                    in->left--, *(in->next)++)
200212397c6Schristos 
201212397c6Schristos /* skip over n bytes of in */
skip(file * in,unsigned n)202212397c6Schristos local void skip(file *in, unsigned n)
203212397c6Schristos {
204212397c6Schristos     unsigned bypass;
205212397c6Schristos 
206212397c6Schristos     if (n > in->left) {
207212397c6Schristos         n -= in->left;
208212397c6Schristos         bypass = n & ~((1U << in->size) - 1);
209212397c6Schristos         if (bypass) {
210212397c6Schristos             if (lseek(in->fd, (off_t)bypass, SEEK_CUR) == -1)
211212397c6Schristos                 bye("seeking ", in->name);
212212397c6Schristos             n -= bypass;
213212397c6Schristos         }
214212397c6Schristos         readmore(in);
215212397c6Schristos         if (n > in->left)
216212397c6Schristos             bye("unexpected end of ", in->name);
217212397c6Schristos     }
218212397c6Schristos     in->left -= n;
219212397c6Schristos     in->next += n;
220212397c6Schristos }
221212397c6Schristos 
222212397c6Schristos /* read a four-byte unsigned integer, little-endian, from in */
read4(file * in)223212397c6Schristos unsigned long read4(file *in)
224212397c6Schristos {
225212397c6Schristos     unsigned long val;
226212397c6Schristos 
227212397c6Schristos     val = read1(in);
228212397c6Schristos     val += (unsigned)read1(in) << 8;
229212397c6Schristos     val += (unsigned long)read1(in) << 16;
230212397c6Schristos     val += (unsigned long)read1(in) << 24;
231212397c6Schristos     return val;
232212397c6Schristos }
233212397c6Schristos 
234212397c6Schristos /* skip over gzip header */
gzheader(file * in)235212397c6Schristos local void gzheader(file *in)
236212397c6Schristos {
237212397c6Schristos     int flags;
238212397c6Schristos     unsigned n;
239212397c6Schristos 
240212397c6Schristos     if (read1(in) != 31 || read1(in) != 139) bye(in->name, " not a gzip file");
241212397c6Schristos     if (read1(in) != 8) bye("unknown compression method in", in->name);
242212397c6Schristos     flags = read1(in);
243212397c6Schristos     if (flags & 0xe0) bye("unknown header flags set in", in->name);
244212397c6Schristos     skip(in, 6);
245212397c6Schristos     if (flags & 4) {
246212397c6Schristos         n = read1(in);
247212397c6Schristos         n += (unsigned)(read1(in)) << 8;
248212397c6Schristos         skip(in, n);
249212397c6Schristos     }
250212397c6Schristos     if (flags & 8) while (read1(in) != 0) ;
251212397c6Schristos     if (flags & 16) while (read1(in) != 0) ;
252212397c6Schristos     if (flags & 2) skip(in, 2);
253212397c6Schristos }
254212397c6Schristos 
255212397c6Schristos /* decompress gzip file "name", return strm with a deflate stream ready to
256212397c6Schristos    continue compression of the data in the gzip file, and return a file
257212397c6Schristos    descriptor pointing to where to write the compressed data -- the deflate
258212397c6Schristos    stream is initialized to compress using level "level" */
gzscan(char * name,z_stream * strm,int level)259212397c6Schristos local int gzscan(char *name, z_stream *strm, int level)
260212397c6Schristos {
261212397c6Schristos     int ret, lastbit, left, full;
262212397c6Schristos     unsigned have;
263212397c6Schristos     unsigned long crc, tot;
264212397c6Schristos     unsigned char *window;
265212397c6Schristos     off_t lastoff, end;
266212397c6Schristos     file gz;
267212397c6Schristos 
268212397c6Schristos     /* open gzip file */
269212397c6Schristos     gz.name = name;
270212397c6Schristos     gz.fd = open(name, O_RDWR, 0);
271212397c6Schristos     if (gz.fd == -1) bye("cannot open ", name);
272212397c6Schristos     gz.buf = malloc(CHUNK);
273212397c6Schristos     if (gz.buf == NULL) bye("out of memory", "");
274212397c6Schristos     gz.size = LGCHUNK;
275212397c6Schristos     gz.left = 0;
276212397c6Schristos 
277212397c6Schristos     /* skip gzip header */
278212397c6Schristos     gzheader(&gz);
279212397c6Schristos 
280212397c6Schristos     /* prepare to decompress */
281212397c6Schristos     window = malloc(DSIZE);
282212397c6Schristos     if (window == NULL) bye("out of memory", "");
283212397c6Schristos     strm->zalloc = Z_NULL;
284212397c6Schristos     strm->zfree = Z_NULL;
285212397c6Schristos     strm->opaque = Z_NULL;
286212397c6Schristos     ret = inflateInit2(strm, -15);
287212397c6Schristos     if (ret != Z_OK) bye("out of memory", " or library mismatch");
288212397c6Schristos 
289212397c6Schristos     /* decompress the deflate stream, saving append information */
290212397c6Schristos     lastbit = 0;
291212397c6Schristos     lastoff = lseek(gz.fd, 0L, SEEK_CUR) - gz.left;
292212397c6Schristos     left = 0;
293212397c6Schristos     strm->avail_in = gz.left;
294212397c6Schristos     strm->next_in = gz.next;
295212397c6Schristos     crc = crc32(0L, Z_NULL, 0);
296212397c6Schristos     have = full = 0;
297212397c6Schristos     do {
298212397c6Schristos         /* if needed, get more input */
299212397c6Schristos         if (strm->avail_in == 0) {
300212397c6Schristos             readmore(&gz);
301212397c6Schristos             strm->avail_in = gz.left;
302212397c6Schristos             strm->next_in = gz.next;
303212397c6Schristos         }
304212397c6Schristos 
305212397c6Schristos         /* set up output to next available section of sliding window */
306212397c6Schristos         strm->avail_out = DSIZE - have;
307212397c6Schristos         strm->next_out = window + have;
308212397c6Schristos 
309212397c6Schristos         /* inflate and check for errors */
310212397c6Schristos         ret = inflate(strm, Z_BLOCK);
311212397c6Schristos         if (ret == Z_STREAM_ERROR) bye("internal stream error!", "");
312212397c6Schristos         if (ret == Z_MEM_ERROR) bye("out of memory", "");
313212397c6Schristos         if (ret == Z_DATA_ERROR)
314212397c6Schristos             bye("invalid compressed data--format violated in", name);
315212397c6Schristos 
316212397c6Schristos         /* update crc and sliding window pointer */
317212397c6Schristos         crc = crc32(crc, window + have, DSIZE - have - strm->avail_out);
318212397c6Schristos         if (strm->avail_out)
319212397c6Schristos             have = DSIZE - strm->avail_out;
320212397c6Schristos         else {
321212397c6Schristos             have = 0;
322212397c6Schristos             full = 1;
323212397c6Schristos         }
324212397c6Schristos 
325212397c6Schristos         /* process end of block */
326212397c6Schristos         if (strm->data_type & 128) {
327212397c6Schristos             if (strm->data_type & 64)
328212397c6Schristos                 left = strm->data_type & 0x1f;
329212397c6Schristos             else {
330212397c6Schristos                 lastbit = strm->data_type & 0x1f;
331212397c6Schristos                 lastoff = lseek(gz.fd, 0L, SEEK_CUR) - strm->avail_in;
332212397c6Schristos             }
333212397c6Schristos         }
334212397c6Schristos     } while (ret != Z_STREAM_END);
335212397c6Schristos     inflateEnd(strm);
336212397c6Schristos     gz.left = strm->avail_in;
337212397c6Schristos     gz.next = strm->next_in;
338212397c6Schristos 
339212397c6Schristos     /* save the location of the end of the compressed data */
340212397c6Schristos     end = lseek(gz.fd, 0L, SEEK_CUR) - gz.left;
341212397c6Schristos 
342212397c6Schristos     /* check gzip trailer and save total for deflate */
343212397c6Schristos     if (crc != read4(&gz))
344212397c6Schristos         bye("invalid compressed data--crc mismatch in ", name);
345212397c6Schristos     tot = strm->total_out;
346212397c6Schristos     if ((tot & 0xffffffffUL) != read4(&gz))
347212397c6Schristos         bye("invalid compressed data--length mismatch in", name);
348212397c6Schristos 
349212397c6Schristos     /* if not at end of file, warn */
350212397c6Schristos     if (gz.left || readin(&gz))
351212397c6Schristos         fprintf(stderr,
352212397c6Schristos             "gzappend warning: junk at end of gzip file overwritten\n");
353212397c6Schristos 
354212397c6Schristos     /* clear last block bit */
355212397c6Schristos     lseek(gz.fd, lastoff - (lastbit != 0), SEEK_SET);
356212397c6Schristos     if (read(gz.fd, gz.buf, 1) != 1) bye("reading after seek on ", name);
357212397c6Schristos     *gz.buf = (unsigned char)(*gz.buf ^ (1 << ((8 - lastbit) & 7)));
358212397c6Schristos     lseek(gz.fd, -1L, SEEK_CUR);
359212397c6Schristos     if (write(gz.fd, gz.buf, 1) != 1) bye("writing after seek to ", name);
360212397c6Schristos 
361212397c6Schristos     /* if window wrapped, build dictionary from window by rotating */
362212397c6Schristos     if (full) {
363212397c6Schristos         rotate(window, DSIZE, have);
364212397c6Schristos         have = DSIZE;
365212397c6Schristos     }
366212397c6Schristos 
367212397c6Schristos     /* set up deflate stream with window, crc, total_in, and leftover bits */
368212397c6Schristos     ret = deflateInit2(strm, level, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
369212397c6Schristos     if (ret != Z_OK) bye("out of memory", "");
370212397c6Schristos     deflateSetDictionary(strm, window, have);
371212397c6Schristos     strm->adler = crc;
372212397c6Schristos     strm->total_in = tot;
373212397c6Schristos     if (left) {
374212397c6Schristos         lseek(gz.fd, --end, SEEK_SET);
375212397c6Schristos         if (read(gz.fd, gz.buf, 1) != 1) bye("reading after seek on ", name);
376212397c6Schristos         deflatePrime(strm, 8 - left, *gz.buf);
377212397c6Schristos     }
378212397c6Schristos     lseek(gz.fd, end, SEEK_SET);
379212397c6Schristos 
380212397c6Schristos     /* clean up and return */
381212397c6Schristos     free(window);
382212397c6Schristos     free(gz.buf);
383212397c6Schristos     return gz.fd;
384212397c6Schristos }
385212397c6Schristos 
386212397c6Schristos /* append file "name" to gzip file gd using deflate stream strm -- if last
387212397c6Schristos    is true, then finish off the deflate stream at the end */
gztack(char * name,int gd,z_stream * strm,int last)388212397c6Schristos local void gztack(char *name, int gd, z_stream *strm, int last)
389212397c6Schristos {
390212397c6Schristos     int fd, len, ret;
391212397c6Schristos     unsigned left;
392212397c6Schristos     unsigned char *in, *out;
393212397c6Schristos 
394212397c6Schristos     /* open file to compress and append */
395212397c6Schristos     fd = 0;
396212397c6Schristos     if (name != NULL) {
397212397c6Schristos         fd = open(name, O_RDONLY, 0);
398212397c6Schristos         if (fd == -1)
399212397c6Schristos             fprintf(stderr, "gzappend warning: %s not found, skipping ...\n",
400212397c6Schristos                     name);
401212397c6Schristos     }
402212397c6Schristos 
403212397c6Schristos     /* allocate buffers */
404ba340e45Schristos     in = malloc(CHUNK);
405212397c6Schristos     out = malloc(CHUNK);
406ba340e45Schristos     if (in == NULL || out == NULL) bye("out of memory", "");
407212397c6Schristos 
408212397c6Schristos     /* compress input file and append to gzip file */
409212397c6Schristos     do {
410212397c6Schristos         /* get more input */
411ba340e45Schristos         len = read(fd, in, CHUNK);
412212397c6Schristos         if (len == -1) {
413212397c6Schristos             fprintf(stderr,
414212397c6Schristos                     "gzappend warning: error reading %s, skipping rest ...\n",
415212397c6Schristos                     name);
416212397c6Schristos             len = 0;
417212397c6Schristos         }
418212397c6Schristos         strm->avail_in = (unsigned)len;
419212397c6Schristos         strm->next_in = in;
420212397c6Schristos         if (len) strm->adler = crc32(strm->adler, in, (unsigned)len);
421212397c6Schristos 
422212397c6Schristos         /* compress and write all available output */
423212397c6Schristos         do {
424212397c6Schristos             strm->avail_out = CHUNK;
425212397c6Schristos             strm->next_out = out;
426212397c6Schristos             ret = deflate(strm, last && len == 0 ? Z_FINISH : Z_NO_FLUSH);
427212397c6Schristos             left = CHUNK - strm->avail_out;
428212397c6Schristos             while (left) {
429212397c6Schristos                 len = write(gd, out + CHUNK - strm->avail_out - left, left);
430212397c6Schristos                 if (len == -1) bye("writing gzip file", "");
431212397c6Schristos                 left -= (unsigned)len;
432212397c6Schristos             }
433212397c6Schristos         } while (strm->avail_out == 0 && ret != Z_STREAM_END);
434212397c6Schristos     } while (len != 0);
435212397c6Schristos 
436212397c6Schristos     /* write trailer after last entry */
437212397c6Schristos     if (last) {
438212397c6Schristos         deflateEnd(strm);
439212397c6Schristos         out[0] = (unsigned char)(strm->adler);
440212397c6Schristos         out[1] = (unsigned char)(strm->adler >> 8);
441212397c6Schristos         out[2] = (unsigned char)(strm->adler >> 16);
442212397c6Schristos         out[3] = (unsigned char)(strm->adler >> 24);
443212397c6Schristos         out[4] = (unsigned char)(strm->total_in);
444212397c6Schristos         out[5] = (unsigned char)(strm->total_in >> 8);
445212397c6Schristos         out[6] = (unsigned char)(strm->total_in >> 16);
446212397c6Schristos         out[7] = (unsigned char)(strm->total_in >> 24);
447212397c6Schristos         len = 8;
448212397c6Schristos         do {
449212397c6Schristos             ret = write(gd, out + 8 - len, len);
450212397c6Schristos             if (ret == -1) bye("writing gzip file", "");
451212397c6Schristos             len -= ret;
452212397c6Schristos         } while (len);
453212397c6Schristos         close(gd);
454212397c6Schristos     }
455212397c6Schristos 
456212397c6Schristos     /* clean up and return */
457212397c6Schristos     free(out);
458ba340e45Schristos     free(in);
459212397c6Schristos     if (fd > 0) close(fd);
460212397c6Schristos }
461212397c6Schristos 
462212397c6Schristos /* process the compression level option if present, scan the gzip file, and
463212397c6Schristos    append the specified files, or append the data from stdin if no other file
464212397c6Schristos    names are provided on the command line -- the gzip file must be writable
465212397c6Schristos    and seekable */
main(int argc,char ** argv)466212397c6Schristos int main(int argc, char **argv)
467212397c6Schristos {
468212397c6Schristos     int gd, level;
469212397c6Schristos     z_stream strm;
470212397c6Schristos 
471212397c6Schristos     /* ignore command name */
472ba340e45Schristos     argc--; argv++;
473212397c6Schristos 
474212397c6Schristos     /* provide usage if no arguments */
475212397c6Schristos     if (*argv == NULL) {
476ba340e45Schristos         printf(
477ba340e45Schristos             "gzappend 1.2 (11 Oct 2012) Copyright (C) 2003, 2012 Mark Adler\n"
478ba340e45Schristos                );
479212397c6Schristos         printf(
480212397c6Schristos             "usage: gzappend [-level] file.gz [ addthis [ andthis ... ]]\n");
481212397c6Schristos         return 0;
482212397c6Schristos     }
483212397c6Schristos 
484212397c6Schristos     /* set compression level */
485212397c6Schristos     level = Z_DEFAULT_COMPRESSION;
486212397c6Schristos     if (argv[0][0] == '-') {
487212397c6Schristos         if (argv[0][1] < '0' || argv[0][1] > '9' || argv[0][2] != 0)
488212397c6Schristos             bye("invalid compression level", "");
489212397c6Schristos         level = argv[0][1] - '0';
490212397c6Schristos         if (*++argv == NULL) bye("no gzip file name after options", "");
491212397c6Schristos     }
492212397c6Schristos 
493212397c6Schristos     /* prepare to append to gzip file */
494212397c6Schristos     gd = gzscan(*argv++, &strm, level);
495212397c6Schristos 
496212397c6Schristos     /* append files on command line, or from stdin if none */
497212397c6Schristos     if (*argv == NULL)
498212397c6Schristos         gztack(NULL, gd, &strm, 1);
499212397c6Schristos     else
500212397c6Schristos         do {
501212397c6Schristos             gztack(*argv, gd, &strm, argv[1] == NULL);
502212397c6Schristos         } while (*++argv != NULL);
503212397c6Schristos     return 0;
504212397c6Schristos }
505