xref: /netbsd-src/external/gpl3/gdb/dist/zlib/examples/gun.c (revision 796c32c94f6e154afc9de0f63da35c91bb739b45)
1212397c6Schristos /* gun.c -- simple gunzip to give an example of the use of inflateBack()
2ba340e45Schristos  * Copyright (C) 2003, 2005, 2008, 2010, 2012 Mark Adler
3212397c6Schristos  * For conditions of distribution and use, see copyright notice in zlib.h
4ba340e45Schristos    Version 1.7  12 August 2012  Mark Adler */
5212397c6Schristos 
6212397c6Schristos /* Version history:
7212397c6Schristos    1.0  16 Feb 2003  First version for testing of inflateBack()
8212397c6Schristos    1.1  21 Feb 2005  Decompress concatenated gzip streams
9212397c6Schristos                      Remove use of "this" variable (C++ keyword)
10212397c6Schristos                      Fix return value for in()
11212397c6Schristos                      Improve allocation failure checking
12212397c6Schristos                      Add typecasting for void * structures
13212397c6Schristos                      Add -h option for command version and usage
14212397c6Schristos                      Add a bunch of comments
15212397c6Schristos    1.2  20 Mar 2005  Add Unix compress (LZW) decompression
16212397c6Schristos                      Copy file attributes from input file to output file
17212397c6Schristos    1.3  12 Jun 2005  Add casts for error messages [Oberhumer]
18212397c6Schristos    1.4   8 Dec 2006  LZW decompression speed improvements
19212397c6Schristos    1.5   9 Feb 2008  Avoid warning in latest version of gcc
20212397c6Schristos    1.6  17 Jan 2010  Avoid signed/unsigned comparison warnings
21ba340e45Schristos    1.7  12 Aug 2012  Update for z_const usage in zlib 1.2.8
22212397c6Schristos  */
23212397c6Schristos 
24212397c6Schristos /*
25212397c6Schristos    gun [ -t ] [ name ... ]
26212397c6Schristos 
27212397c6Schristos    decompresses the data in the named gzip files.  If no arguments are given,
28212397c6Schristos    gun will decompress from stdin to stdout.  The names must end in .gz, -gz,
29212397c6Schristos    .z, -z, _z, or .Z.  The uncompressed data will be written to a file name
30212397c6Schristos    with the suffix stripped.  On success, the original file is deleted.  On
31212397c6Schristos    failure, the output file is deleted.  For most failures, the command will
32212397c6Schristos    continue to process the remaining names on the command line.  A memory
33212397c6Schristos    allocation failure will abort the command.  If -t is specified, then the
34212397c6Schristos    listed files or stdin will be tested as gzip files for integrity (without
35212397c6Schristos    checking for a proper suffix), no output will be written, and no files
36212397c6Schristos    will be deleted.
37212397c6Schristos 
38212397c6Schristos    Like gzip, gun allows concatenated gzip streams and will decompress them,
39212397c6Schristos    writing all of the uncompressed data to the output.  Unlike gzip, gun allows
40212397c6Schristos    an empty file on input, and will produce no error writing an empty output
41212397c6Schristos    file.
42212397c6Schristos 
43212397c6Schristos    gun will also decompress files made by Unix compress, which uses LZW
44212397c6Schristos    compression.  These files are automatically detected by virtue of their
45212397c6Schristos    magic header bytes.  Since the end of Unix compress stream is marked by the
46212397c6Schristos    end-of-file, they cannot be concantenated.  If a Unix compress stream is
47212397c6Schristos    encountered in an input file, it is the last stream in that file.
48212397c6Schristos 
49*796c32c9Schristos    Like gunzip and uncompress, the file attributes of the original compressed
50212397c6Schristos    file are maintained in the final uncompressed file, to the extent that the
51212397c6Schristos    user permissions allow it.
52212397c6Schristos 
53212397c6Schristos    On my Mac OS X PowerPC G4, gun is almost twice as fast as gunzip (version
54212397c6Schristos    1.2.4) is on the same file, when gun is linked with zlib 1.2.2.  Also the
55212397c6Schristos    LZW decompression provided by gun is about twice as fast as the standard
56212397c6Schristos    Unix uncompress command.
57212397c6Schristos  */
58212397c6Schristos 
59212397c6Schristos /* external functions and related types and constants */
60212397c6Schristos #include <stdio.h>          /* fprintf() */
61212397c6Schristos #include <stdlib.h>         /* malloc(), free() */
62212397c6Schristos #include <string.h>         /* strerror(), strcmp(), strlen(), memcpy() */
63212397c6Schristos #include <errno.h>          /* errno */
64212397c6Schristos #include <fcntl.h>          /* open() */
65212397c6Schristos #include <unistd.h>         /* read(), write(), close(), chown(), unlink() */
66212397c6Schristos #include <sys/types.h>
67212397c6Schristos #include <sys/stat.h>       /* stat(), chmod() */
68212397c6Schristos #include <utime.h>          /* utime() */
69212397c6Schristos #include "zlib.h"           /* inflateBackInit(), inflateBack(), */
70212397c6Schristos                             /* inflateBackEnd(), crc32() */
71212397c6Schristos 
72212397c6Schristos /* function declaration */
73212397c6Schristos #define local static
74212397c6Schristos 
75212397c6Schristos /* buffer constants */
76212397c6Schristos #define SIZE 32768U         /* input and output buffer sizes */
77212397c6Schristos #define PIECE 16384         /* limits i/o chunks for 16-bit int case */
78212397c6Schristos 
79212397c6Schristos /* structure for infback() to pass to input function in() -- it maintains the
80212397c6Schristos    input file and a buffer of size SIZE */
81212397c6Schristos struct ind {
82212397c6Schristos     int infile;
83212397c6Schristos     unsigned char *inbuf;
84212397c6Schristos };
85212397c6Schristos 
86212397c6Schristos /* Load input buffer, assumed to be empty, and return bytes loaded and a
87212397c6Schristos    pointer to them.  read() is called until the buffer is full, or until it
88212397c6Schristos    returns end-of-file or error.  Return 0 on error. */
in(void * in_desc,z_const unsigned char ** buf)89ba340e45Schristos local unsigned in(void *in_desc, z_const unsigned char **buf)
90212397c6Schristos {
91212397c6Schristos     int ret;
92212397c6Schristos     unsigned len;
93212397c6Schristos     unsigned char *next;
94212397c6Schristos     struct ind *me = (struct ind *)in_desc;
95212397c6Schristos 
96212397c6Schristos     next = me->inbuf;
97212397c6Schristos     *buf = next;
98212397c6Schristos     len = 0;
99212397c6Schristos     do {
100212397c6Schristos         ret = PIECE;
101212397c6Schristos         if ((unsigned)ret > SIZE - len)
102212397c6Schristos             ret = (int)(SIZE - len);
103212397c6Schristos         ret = (int)read(me->infile, next, ret);
104212397c6Schristos         if (ret == -1) {
105212397c6Schristos             len = 0;
106212397c6Schristos             break;
107212397c6Schristos         }
108212397c6Schristos         next += ret;
109212397c6Schristos         len += ret;
110212397c6Schristos     } while (ret != 0 && len < SIZE);
111212397c6Schristos     return len;
112212397c6Schristos }
113212397c6Schristos 
114212397c6Schristos /* structure for infback() to pass to output function out() -- it maintains the
115212397c6Schristos    output file, a running CRC-32 check on the output and the total number of
116212397c6Schristos    bytes output, both for checking against the gzip trailer.  (The length in
117212397c6Schristos    the gzip trailer is stored modulo 2^32, so it's ok if a long is 32 bits and
118212397c6Schristos    the output is greater than 4 GB.) */
119212397c6Schristos struct outd {
120212397c6Schristos     int outfile;
121212397c6Schristos     int check;                  /* true if checking crc and total */
122212397c6Schristos     unsigned long crc;
123212397c6Schristos     unsigned long total;
124212397c6Schristos };
125212397c6Schristos 
126212397c6Schristos /* Write output buffer and update the CRC-32 and total bytes written.  write()
127212397c6Schristos    is called until all of the output is written or an error is encountered.
128212397c6Schristos    On success out() returns 0.  For a write failure, out() returns 1.  If the
129212397c6Schristos    output file descriptor is -1, then nothing is written.
130212397c6Schristos  */
out(void * out_desc,unsigned char * buf,unsigned len)131212397c6Schristos local int out(void *out_desc, unsigned char *buf, unsigned len)
132212397c6Schristos {
133212397c6Schristos     int ret;
134212397c6Schristos     struct outd *me = (struct outd *)out_desc;
135212397c6Schristos 
136212397c6Schristos     if (me->check) {
137212397c6Schristos         me->crc = crc32(me->crc, buf, len);
138212397c6Schristos         me->total += len;
139212397c6Schristos     }
140212397c6Schristos     if (me->outfile != -1)
141212397c6Schristos         do {
142212397c6Schristos             ret = PIECE;
143212397c6Schristos             if ((unsigned)ret > len)
144212397c6Schristos                 ret = (int)len;
145212397c6Schristos             ret = (int)write(me->outfile, buf, ret);
146212397c6Schristos             if (ret == -1)
147212397c6Schristos                 return 1;
148212397c6Schristos             buf += ret;
149212397c6Schristos             len -= ret;
150212397c6Schristos         } while (len != 0);
151212397c6Schristos     return 0;
152212397c6Schristos }
153212397c6Schristos 
154212397c6Schristos /* next input byte macro for use inside lunpipe() and gunpipe() */
155212397c6Schristos #define NEXT() (have ? 0 : (have = in(indp, &next)), \
156212397c6Schristos                 last = have ? (have--, (int)(*next++)) : -1)
157212397c6Schristos 
158212397c6Schristos /* memory for gunpipe() and lunpipe() --
159212397c6Schristos    the first 256 entries of prefix[] and suffix[] are never used, could
160212397c6Schristos    have offset the index, but it's faster to waste the memory */
161212397c6Schristos unsigned char inbuf[SIZE];              /* input buffer */
162212397c6Schristos unsigned char outbuf[SIZE];             /* output buffer */
163212397c6Schristos unsigned short prefix[65536];           /* index to LZW prefix string */
164212397c6Schristos unsigned char suffix[65536];            /* one-character LZW suffix */
165212397c6Schristos unsigned char match[65280 + 2];         /* buffer for reversed match or gzip
166212397c6Schristos                                            32K sliding window */
167212397c6Schristos 
168212397c6Schristos /* throw out what's left in the current bits byte buffer (this is a vestigial
169212397c6Schristos    aspect of the compressed data format derived from an implementation that
170212397c6Schristos    made use of a special VAX machine instruction!) */
171212397c6Schristos #define FLUSHCODE() \
172212397c6Schristos     do { \
173212397c6Schristos         left = 0; \
174212397c6Schristos         rem = 0; \
175212397c6Schristos         if (chunk > have) { \
176212397c6Schristos             chunk -= have; \
177212397c6Schristos             have = 0; \
178212397c6Schristos             if (NEXT() == -1) \
179212397c6Schristos                 break; \
180212397c6Schristos             chunk--; \
181212397c6Schristos             if (chunk > have) { \
182212397c6Schristos                 chunk = have = 0; \
183212397c6Schristos                 break; \
184212397c6Schristos             } \
185212397c6Schristos         } \
186212397c6Schristos         have -= chunk; \
187212397c6Schristos         next += chunk; \
188212397c6Schristos         chunk = 0; \
189212397c6Schristos     } while (0)
190212397c6Schristos 
191212397c6Schristos /* Decompress a compress (LZW) file from indp to outfile.  The compress magic
192212397c6Schristos    header (two bytes) has already been read and verified.  There are have bytes
193212397c6Schristos    of buffered input at next.  strm is used for passing error information back
194212397c6Schristos    to gunpipe().
195212397c6Schristos 
196212397c6Schristos    lunpipe() will return Z_OK on success, Z_BUF_ERROR for an unexpected end of
197212397c6Schristos    file, read error, or write error (a write error indicated by strm->next_in
198212397c6Schristos    not equal to Z_NULL), or Z_DATA_ERROR for invalid input.
199212397c6Schristos  */
lunpipe(unsigned have,z_const unsigned char * next,struct ind * indp,int outfile,z_stream * strm)200ba340e45Schristos local int lunpipe(unsigned have, z_const unsigned char *next, struct ind *indp,
201212397c6Schristos                   int outfile, z_stream *strm)
202212397c6Schristos {
203212397c6Schristos     int last;                   /* last byte read by NEXT(), or -1 if EOF */
204212397c6Schristos     unsigned chunk;             /* bytes left in current chunk */
205212397c6Schristos     int left;                   /* bits left in rem */
206212397c6Schristos     unsigned rem;               /* unused bits from input */
207212397c6Schristos     int bits;                   /* current bits per code */
208212397c6Schristos     unsigned code;              /* code, table traversal index */
209212397c6Schristos     unsigned mask;              /* mask for current bits codes */
210212397c6Schristos     int max;                    /* maximum bits per code for this stream */
211212397c6Schristos     unsigned flags;             /* compress flags, then block compress flag */
212212397c6Schristos     unsigned end;               /* last valid entry in prefix/suffix tables */
213212397c6Schristos     unsigned temp;              /* current code */
214212397c6Schristos     unsigned prev;              /* previous code */
215212397c6Schristos     unsigned final;             /* last character written for previous code */
216212397c6Schristos     unsigned stack;             /* next position for reversed string */
217212397c6Schristos     unsigned outcnt;            /* bytes in output buffer */
218212397c6Schristos     struct outd outd;           /* output structure */
219212397c6Schristos     unsigned char *p;
220212397c6Schristos 
221212397c6Schristos     /* set up output */
222212397c6Schristos     outd.outfile = outfile;
223212397c6Schristos     outd.check = 0;
224212397c6Schristos 
225212397c6Schristos     /* process remainder of compress header -- a flags byte */
226212397c6Schristos     flags = NEXT();
227212397c6Schristos     if (last == -1)
228212397c6Schristos         return Z_BUF_ERROR;
229212397c6Schristos     if (flags & 0x60) {
230212397c6Schristos         strm->msg = (char *)"unknown lzw flags set";
231212397c6Schristos         return Z_DATA_ERROR;
232212397c6Schristos     }
233212397c6Schristos     max = flags & 0x1f;
234212397c6Schristos     if (max < 9 || max > 16) {
235212397c6Schristos         strm->msg = (char *)"lzw bits out of range";
236212397c6Schristos         return Z_DATA_ERROR;
237212397c6Schristos     }
238212397c6Schristos     if (max == 9)                           /* 9 doesn't really mean 9 */
239212397c6Schristos         max = 10;
240212397c6Schristos     flags &= 0x80;                          /* true if block compress */
241212397c6Schristos 
242212397c6Schristos     /* clear table */
243212397c6Schristos     bits = 9;
244212397c6Schristos     mask = 0x1ff;
245212397c6Schristos     end = flags ? 256 : 255;
246212397c6Schristos 
247212397c6Schristos     /* set up: get first 9-bit code, which is the first decompressed byte, but
248212397c6Schristos        don't create a table entry until the next code */
249212397c6Schristos     if (NEXT() == -1)                       /* no compressed data is ok */
250212397c6Schristos         return Z_OK;
251212397c6Schristos     final = prev = (unsigned)last;          /* low 8 bits of code */
252212397c6Schristos     if (NEXT() == -1)                       /* missing a bit */
253212397c6Schristos         return Z_BUF_ERROR;
254212397c6Schristos     if (last & 1) {                         /* code must be < 256 */
255212397c6Schristos         strm->msg = (char *)"invalid lzw code";
256212397c6Schristos         return Z_DATA_ERROR;
257212397c6Schristos     }
258212397c6Schristos     rem = (unsigned)last >> 1;              /* remaining 7 bits */
259212397c6Schristos     left = 7;
260212397c6Schristos     chunk = bits - 2;                       /* 7 bytes left in this chunk */
261212397c6Schristos     outbuf[0] = (unsigned char)final;       /* write first decompressed byte */
262212397c6Schristos     outcnt = 1;
263212397c6Schristos 
264212397c6Schristos     /* decode codes */
265212397c6Schristos     stack = 0;
266212397c6Schristos     for (;;) {
267212397c6Schristos         /* if the table will be full after this, increment the code size */
268212397c6Schristos         if (end >= mask && bits < max) {
269212397c6Schristos             FLUSHCODE();
270212397c6Schristos             bits++;
271212397c6Schristos             mask <<= 1;
272212397c6Schristos             mask++;
273212397c6Schristos         }
274212397c6Schristos 
275212397c6Schristos         /* get a code of length bits */
276212397c6Schristos         if (chunk == 0)                     /* decrement chunk modulo bits */
277212397c6Schristos             chunk = bits;
278212397c6Schristos         code = rem;                         /* low bits of code */
279212397c6Schristos         if (NEXT() == -1) {                 /* EOF is end of compressed data */
280212397c6Schristos             /* write remaining buffered output */
281212397c6Schristos             if (outcnt && out(&outd, outbuf, outcnt)) {
282212397c6Schristos                 strm->next_in = outbuf;     /* signal write error */
283212397c6Schristos                 return Z_BUF_ERROR;
284212397c6Schristos             }
285212397c6Schristos             return Z_OK;
286212397c6Schristos         }
287212397c6Schristos         code += (unsigned)last << left;     /* middle (or high) bits of code */
288212397c6Schristos         left += 8;
289212397c6Schristos         chunk--;
290212397c6Schristos         if (bits > left) {                  /* need more bits */
291212397c6Schristos             if (NEXT() == -1)               /* can't end in middle of code */
292212397c6Schristos                 return Z_BUF_ERROR;
293212397c6Schristos             code += (unsigned)last << left; /* high bits of code */
294212397c6Schristos             left += 8;
295212397c6Schristos             chunk--;
296212397c6Schristos         }
297212397c6Schristos         code &= mask;                       /* mask to current code length */
298212397c6Schristos         left -= bits;                       /* number of unused bits */
299212397c6Schristos         rem = (unsigned)last >> (8 - left); /* unused bits from last byte */
300212397c6Schristos 
301212397c6Schristos         /* process clear code (256) */
302212397c6Schristos         if (code == 256 && flags) {
303212397c6Schristos             FLUSHCODE();
304212397c6Schristos             bits = 9;                       /* initialize bits and mask */
305212397c6Schristos             mask = 0x1ff;
306212397c6Schristos             end = 255;                      /* empty table */
307212397c6Schristos             continue;                       /* get next code */
308212397c6Schristos         }
309212397c6Schristos 
310212397c6Schristos         /* special code to reuse last match */
311212397c6Schristos         temp = code;                        /* save the current code */
312212397c6Schristos         if (code > end) {
313212397c6Schristos             /* Be picky on the allowed code here, and make sure that the code
314212397c6Schristos                we drop through (prev) will be a valid index so that random
315212397c6Schristos                input does not cause an exception.  The code != end + 1 check is
316212397c6Schristos                empirically derived, and not checked in the original uncompress
317212397c6Schristos                code.  If this ever causes a problem, that check could be safely
318212397c6Schristos                removed.  Leaving this check in greatly improves gun's ability
319212397c6Schristos                to detect random or corrupted input after a compress header.
320212397c6Schristos                In any case, the prev > end check must be retained. */
321212397c6Schristos             if (code != end + 1 || prev > end) {
322212397c6Schristos                 strm->msg = (char *)"invalid lzw code";
323212397c6Schristos                 return Z_DATA_ERROR;
324212397c6Schristos             }
325212397c6Schristos             match[stack++] = (unsigned char)final;
326212397c6Schristos             code = prev;
327212397c6Schristos         }
328212397c6Schristos 
329212397c6Schristos         /* walk through linked list to generate output in reverse order */
330212397c6Schristos         p = match + stack;
331212397c6Schristos         while (code >= 256) {
332212397c6Schristos             *p++ = suffix[code];
333212397c6Schristos             code = prefix[code];
334212397c6Schristos         }
335212397c6Schristos         stack = p - match;
336212397c6Schristos         match[stack++] = (unsigned char)code;
337212397c6Schristos         final = code;
338212397c6Schristos 
339212397c6Schristos         /* link new table entry */
340212397c6Schristos         if (end < mask) {
341212397c6Schristos             end++;
342212397c6Schristos             prefix[end] = (unsigned short)prev;
343212397c6Schristos             suffix[end] = (unsigned char)final;
344212397c6Schristos         }
345212397c6Schristos 
346212397c6Schristos         /* set previous code for next iteration */
347212397c6Schristos         prev = temp;
348212397c6Schristos 
349212397c6Schristos         /* write output in forward order */
350212397c6Schristos         while (stack > SIZE - outcnt) {
351212397c6Schristos             while (outcnt < SIZE)
352212397c6Schristos                 outbuf[outcnt++] = match[--stack];
353212397c6Schristos             if (out(&outd, outbuf, outcnt)) {
354212397c6Schristos                 strm->next_in = outbuf; /* signal write error */
355212397c6Schristos                 return Z_BUF_ERROR;
356212397c6Schristos             }
357212397c6Schristos             outcnt = 0;
358212397c6Schristos         }
359212397c6Schristos         p = match + stack;
360212397c6Schristos         do {
361212397c6Schristos             outbuf[outcnt++] = *--p;
362212397c6Schristos         } while (p > match);
363212397c6Schristos         stack = 0;
364212397c6Schristos 
365212397c6Schristos         /* loop for next code with final and prev as the last match, rem and
366212397c6Schristos            left provide the first 0..7 bits of the next code, end is the last
367212397c6Schristos            valid table entry */
368212397c6Schristos     }
369212397c6Schristos }
370212397c6Schristos 
371212397c6Schristos /* Decompress a gzip file from infile to outfile.  strm is assumed to have been
372212397c6Schristos    successfully initialized with inflateBackInit().  The input file may consist
373212397c6Schristos    of a series of gzip streams, in which case all of them will be decompressed
374212397c6Schristos    to the output file.  If outfile is -1, then the gzip stream(s) integrity is
375212397c6Schristos    checked and nothing is written.
376212397c6Schristos 
377212397c6Schristos    The return value is a zlib error code: Z_MEM_ERROR if out of memory,
378212397c6Schristos    Z_DATA_ERROR if the header or the compressed data is invalid, or if the
379212397c6Schristos    trailer CRC-32 check or length doesn't match, Z_BUF_ERROR if the input ends
380212397c6Schristos    prematurely or a write error occurs, or Z_ERRNO if junk (not a another gzip
381212397c6Schristos    stream) follows a valid gzip stream.
382212397c6Schristos  */
gunpipe(z_stream * strm,int infile,int outfile)383212397c6Schristos local int gunpipe(z_stream *strm, int infile, int outfile)
384212397c6Schristos {
385212397c6Schristos     int ret, first, last;
386212397c6Schristos     unsigned have, flags, len;
387ba340e45Schristos     z_const unsigned char *next = NULL;
388212397c6Schristos     struct ind ind, *indp;
389212397c6Schristos     struct outd outd;
390212397c6Schristos 
391212397c6Schristos     /* setup input buffer */
392212397c6Schristos     ind.infile = infile;
393212397c6Schristos     ind.inbuf = inbuf;
394212397c6Schristos     indp = &ind;
395212397c6Schristos 
396212397c6Schristos     /* decompress concatenated gzip streams */
397212397c6Schristos     have = 0;                               /* no input data read in yet */
398212397c6Schristos     first = 1;                              /* looking for first gzip header */
399212397c6Schristos     strm->next_in = Z_NULL;                 /* so Z_BUF_ERROR means EOF */
400212397c6Schristos     for (;;) {
401212397c6Schristos         /* look for the two magic header bytes for a gzip stream */
402212397c6Schristos         if (NEXT() == -1) {
403212397c6Schristos             ret = Z_OK;
404212397c6Schristos             break;                          /* empty gzip stream is ok */
405212397c6Schristos         }
406212397c6Schristos         if (last != 31 || (NEXT() != 139 && last != 157)) {
407212397c6Schristos             strm->msg = (char *)"incorrect header check";
408212397c6Schristos             ret = first ? Z_DATA_ERROR : Z_ERRNO;
409212397c6Schristos             break;                          /* not a gzip or compress header */
410212397c6Schristos         }
411212397c6Schristos         first = 0;                          /* next non-header is junk */
412212397c6Schristos 
413212397c6Schristos         /* process a compress (LZW) file -- can't be concatenated after this */
414212397c6Schristos         if (last == 157) {
415212397c6Schristos             ret = lunpipe(have, next, indp, outfile, strm);
416212397c6Schristos             break;
417212397c6Schristos         }
418212397c6Schristos 
419212397c6Schristos         /* process remainder of gzip header */
420212397c6Schristos         ret = Z_BUF_ERROR;
421212397c6Schristos         if (NEXT() != 8) {                  /* only deflate method allowed */
422212397c6Schristos             if (last == -1) break;
423212397c6Schristos             strm->msg = (char *)"unknown compression method";
424212397c6Schristos             ret = Z_DATA_ERROR;
425212397c6Schristos             break;
426212397c6Schristos         }
427212397c6Schristos         flags = NEXT();                     /* header flags */
428212397c6Schristos         NEXT();                             /* discard mod time, xflgs, os */
429212397c6Schristos         NEXT();
430212397c6Schristos         NEXT();
431212397c6Schristos         NEXT();
432212397c6Schristos         NEXT();
433212397c6Schristos         NEXT();
434212397c6Schristos         if (last == -1) break;
435212397c6Schristos         if (flags & 0xe0) {
436212397c6Schristos             strm->msg = (char *)"unknown header flags set";
437212397c6Schristos             ret = Z_DATA_ERROR;
438212397c6Schristos             break;
439212397c6Schristos         }
440212397c6Schristos         if (flags & 4) {                    /* extra field */
441212397c6Schristos             len = NEXT();
442212397c6Schristos             len += (unsigned)(NEXT()) << 8;
443212397c6Schristos             if (last == -1) break;
444212397c6Schristos             while (len > have) {
445212397c6Schristos                 len -= have;
446212397c6Schristos                 have = 0;
447212397c6Schristos                 if (NEXT() == -1) break;
448212397c6Schristos                 len--;
449212397c6Schristos             }
450212397c6Schristos             if (last == -1) break;
451212397c6Schristos             have -= len;
452212397c6Schristos             next += len;
453212397c6Schristos         }
454212397c6Schristos         if (flags & 8)                      /* file name */
455212397c6Schristos             while (NEXT() != 0 && last != -1)
456212397c6Schristos                 ;
457212397c6Schristos         if (flags & 16)                     /* comment */
458212397c6Schristos             while (NEXT() != 0 && last != -1)
459212397c6Schristos                 ;
460212397c6Schristos         if (flags & 2) {                    /* header crc */
461212397c6Schristos             NEXT();
462212397c6Schristos             NEXT();
463212397c6Schristos         }
464212397c6Schristos         if (last == -1) break;
465212397c6Schristos 
466212397c6Schristos         /* set up output */
467212397c6Schristos         outd.outfile = outfile;
468212397c6Schristos         outd.check = 1;
469212397c6Schristos         outd.crc = crc32(0L, Z_NULL, 0);
470212397c6Schristos         outd.total = 0;
471212397c6Schristos 
472212397c6Schristos         /* decompress data to output */
473212397c6Schristos         strm->next_in = next;
474212397c6Schristos         strm->avail_in = have;
475212397c6Schristos         ret = inflateBack(strm, in, indp, out, &outd);
476212397c6Schristos         if (ret != Z_STREAM_END) break;
477212397c6Schristos         next = strm->next_in;
478212397c6Schristos         have = strm->avail_in;
479212397c6Schristos         strm->next_in = Z_NULL;             /* so Z_BUF_ERROR means EOF */
480212397c6Schristos 
481212397c6Schristos         /* check trailer */
482212397c6Schristos         ret = Z_BUF_ERROR;
483212397c6Schristos         if (NEXT() != (int)(outd.crc & 0xff) ||
484212397c6Schristos             NEXT() != (int)((outd.crc >> 8) & 0xff) ||
485212397c6Schristos             NEXT() != (int)((outd.crc >> 16) & 0xff) ||
486212397c6Schristos             NEXT() != (int)((outd.crc >> 24) & 0xff)) {
487212397c6Schristos             /* crc error */
488212397c6Schristos             if (last != -1) {
489212397c6Schristos                 strm->msg = (char *)"incorrect data check";
490212397c6Schristos                 ret = Z_DATA_ERROR;
491212397c6Schristos             }
492212397c6Schristos             break;
493212397c6Schristos         }
494212397c6Schristos         if (NEXT() != (int)(outd.total & 0xff) ||
495212397c6Schristos             NEXT() != (int)((outd.total >> 8) & 0xff) ||
496212397c6Schristos             NEXT() != (int)((outd.total >> 16) & 0xff) ||
497212397c6Schristos             NEXT() != (int)((outd.total >> 24) & 0xff)) {
498212397c6Schristos             /* length error */
499212397c6Schristos             if (last != -1) {
500212397c6Schristos                 strm->msg = (char *)"incorrect length check";
501212397c6Schristos                 ret = Z_DATA_ERROR;
502212397c6Schristos             }
503212397c6Schristos             break;
504212397c6Schristos         }
505212397c6Schristos 
506212397c6Schristos         /* go back and look for another gzip stream */
507212397c6Schristos     }
508212397c6Schristos 
509212397c6Schristos     /* clean up and return */
510212397c6Schristos     return ret;
511212397c6Schristos }
512212397c6Schristos 
513212397c6Schristos /* Copy file attributes, from -> to, as best we can.  This is best effort, so
514212397c6Schristos    no errors are reported.  The mode bits, including suid, sgid, and the sticky
515212397c6Schristos    bit are copied (if allowed), the owner's user id and group id are copied
516212397c6Schristos    (again if allowed), and the access and modify times are copied. */
copymeta(char * from,char * to)517212397c6Schristos local void copymeta(char *from, char *to)
518212397c6Schristos {
519212397c6Schristos     struct stat was;
520212397c6Schristos     struct utimbuf when;
521212397c6Schristos 
522212397c6Schristos     /* get all of from's Unix meta data, return if not a regular file */
523212397c6Schristos     if (stat(from, &was) != 0 || (was.st_mode & S_IFMT) != S_IFREG)
524212397c6Schristos         return;
525212397c6Schristos 
526212397c6Schristos     /* set to's mode bits, ignore errors */
527212397c6Schristos     (void)chmod(to, was.st_mode & 07777);
528212397c6Schristos 
529212397c6Schristos     /* copy owner's user and group, ignore errors */
530212397c6Schristos     (void)chown(to, was.st_uid, was.st_gid);
531212397c6Schristos 
532212397c6Schristos     /* copy access and modify times, ignore errors */
533212397c6Schristos     when.actime = was.st_atime;
534212397c6Schristos     when.modtime = was.st_mtime;
535212397c6Schristos     (void)utime(to, &when);
536212397c6Schristos }
537212397c6Schristos 
538212397c6Schristos /* Decompress the file inname to the file outnname, of if test is true, just
539212397c6Schristos    decompress without writing and check the gzip trailer for integrity.  If
540212397c6Schristos    inname is NULL or an empty string, read from stdin.  If outname is NULL or
541212397c6Schristos    an empty string, write to stdout.  strm is a pre-initialized inflateBack
542212397c6Schristos    structure.  When appropriate, copy the file attributes from inname to
543212397c6Schristos    outname.
544212397c6Schristos 
545212397c6Schristos    gunzip() returns 1 if there is an out-of-memory error or an unexpected
546212397c6Schristos    return code from gunpipe().  Otherwise it returns 0.
547212397c6Schristos  */
gunzip(z_stream * strm,char * inname,char * outname,int test)548212397c6Schristos local int gunzip(z_stream *strm, char *inname, char *outname, int test)
549212397c6Schristos {
550212397c6Schristos     int ret;
551212397c6Schristos     int infile, outfile;
552212397c6Schristos 
553212397c6Schristos     /* open files */
554212397c6Schristos     if (inname == NULL || *inname == 0) {
555212397c6Schristos         inname = "-";
556212397c6Schristos         infile = 0;     /* stdin */
557212397c6Schristos     }
558212397c6Schristos     else {
559212397c6Schristos         infile = open(inname, O_RDONLY, 0);
560212397c6Schristos         if (infile == -1) {
561212397c6Schristos             fprintf(stderr, "gun cannot open %s\n", inname);
562212397c6Schristos             return 0;
563212397c6Schristos         }
564212397c6Schristos     }
565212397c6Schristos     if (test)
566212397c6Schristos         outfile = -1;
567212397c6Schristos     else if (outname == NULL || *outname == 0) {
568212397c6Schristos         outname = "-";
569212397c6Schristos         outfile = 1;    /* stdout */
570212397c6Schristos     }
571212397c6Schristos     else {
572212397c6Schristos         outfile = open(outname, O_CREAT | O_TRUNC | O_WRONLY, 0666);
573212397c6Schristos         if (outfile == -1) {
574212397c6Schristos             close(infile);
575212397c6Schristos             fprintf(stderr, "gun cannot create %s\n", outname);
576212397c6Schristos             return 0;
577212397c6Schristos         }
578212397c6Schristos     }
579212397c6Schristos     errno = 0;
580212397c6Schristos 
581212397c6Schristos     /* decompress */
582212397c6Schristos     ret = gunpipe(strm, infile, outfile);
583212397c6Schristos     if (outfile > 2) close(outfile);
584212397c6Schristos     if (infile > 2) close(infile);
585212397c6Schristos 
586212397c6Schristos     /* interpret result */
587212397c6Schristos     switch (ret) {
588212397c6Schristos     case Z_OK:
589212397c6Schristos     case Z_ERRNO:
590212397c6Schristos         if (infile > 2 && outfile > 2) {
591212397c6Schristos             copymeta(inname, outname);          /* copy attributes */
592212397c6Schristos             unlink(inname);
593212397c6Schristos         }
594212397c6Schristos         if (ret == Z_ERRNO)
595212397c6Schristos             fprintf(stderr, "gun warning: trailing garbage ignored in %s\n",
596212397c6Schristos                     inname);
597212397c6Schristos         break;
598212397c6Schristos     case Z_DATA_ERROR:
599212397c6Schristos         if (outfile > 2) unlink(outname);
600212397c6Schristos         fprintf(stderr, "gun data error on %s: %s\n", inname, strm->msg);
601212397c6Schristos         break;
602212397c6Schristos     case Z_MEM_ERROR:
603212397c6Schristos         if (outfile > 2) unlink(outname);
604212397c6Schristos         fprintf(stderr, "gun out of memory error--aborting\n");
605212397c6Schristos         return 1;
606212397c6Schristos     case Z_BUF_ERROR:
607212397c6Schristos         if (outfile > 2) unlink(outname);
608212397c6Schristos         if (strm->next_in != Z_NULL) {
609212397c6Schristos             fprintf(stderr, "gun write error on %s: %s\n",
610212397c6Schristos                     outname, strerror(errno));
611212397c6Schristos         }
612212397c6Schristos         else if (errno) {
613212397c6Schristos             fprintf(stderr, "gun read error on %s: %s\n",
614212397c6Schristos                     inname, strerror(errno));
615212397c6Schristos         }
616212397c6Schristos         else {
617212397c6Schristos             fprintf(stderr, "gun unexpected end of file on %s\n",
618212397c6Schristos                     inname);
619212397c6Schristos         }
620212397c6Schristos         break;
621212397c6Schristos     default:
622212397c6Schristos         if (outfile > 2) unlink(outname);
623212397c6Schristos         fprintf(stderr, "gun internal error--aborting\n");
624212397c6Schristos         return 1;
625212397c6Schristos     }
626212397c6Schristos     return 0;
627212397c6Schristos }
628212397c6Schristos 
629212397c6Schristos /* Process the gun command line arguments.  See the command syntax near the
630212397c6Schristos    beginning of this source file. */
main(int argc,char ** argv)631212397c6Schristos int main(int argc, char **argv)
632212397c6Schristos {
633212397c6Schristos     int ret, len, test;
634212397c6Schristos     char *outname;
635212397c6Schristos     unsigned char *window;
636212397c6Schristos     z_stream strm;
637212397c6Schristos 
638212397c6Schristos     /* initialize inflateBack state for repeated use */
639212397c6Schristos     window = match;                         /* reuse LZW match buffer */
640212397c6Schristos     strm.zalloc = Z_NULL;
641212397c6Schristos     strm.zfree = Z_NULL;
642212397c6Schristos     strm.opaque = Z_NULL;
643212397c6Schristos     ret = inflateBackInit(&strm, 15, window);
644212397c6Schristos     if (ret != Z_OK) {
645212397c6Schristos         fprintf(stderr, "gun out of memory error--aborting\n");
646212397c6Schristos         return 1;
647212397c6Schristos     }
648212397c6Schristos 
649212397c6Schristos     /* decompress each file to the same name with the suffix removed */
650212397c6Schristos     argc--;
651212397c6Schristos     argv++;
652212397c6Schristos     test = 0;
653212397c6Schristos     if (argc && strcmp(*argv, "-h") == 0) {
654212397c6Schristos         fprintf(stderr, "gun 1.6 (17 Jan 2010)\n");
655212397c6Schristos         fprintf(stderr, "Copyright (C) 2003-2010 Mark Adler\n");
656212397c6Schristos         fprintf(stderr, "usage: gun [-t] [file1.gz [file2.Z ...]]\n");
657212397c6Schristos         return 0;
658212397c6Schristos     }
659212397c6Schristos     if (argc && strcmp(*argv, "-t") == 0) {
660212397c6Schristos         test = 1;
661212397c6Schristos         argc--;
662212397c6Schristos         argv++;
663212397c6Schristos     }
664212397c6Schristos     if (argc)
665212397c6Schristos         do {
666212397c6Schristos             if (test)
667212397c6Schristos                 outname = NULL;
668212397c6Schristos             else {
669212397c6Schristos                 len = (int)strlen(*argv);
670212397c6Schristos                 if (strcmp(*argv + len - 3, ".gz") == 0 ||
671212397c6Schristos                     strcmp(*argv + len - 3, "-gz") == 0)
672212397c6Schristos                     len -= 3;
673212397c6Schristos                 else if (strcmp(*argv + len - 2, ".z") == 0 ||
674212397c6Schristos                     strcmp(*argv + len - 2, "-z") == 0 ||
675212397c6Schristos                     strcmp(*argv + len - 2, "_z") == 0 ||
676212397c6Schristos                     strcmp(*argv + len - 2, ".Z") == 0)
677212397c6Schristos                     len -= 2;
678212397c6Schristos                 else {
679212397c6Schristos                     fprintf(stderr, "gun error: no gz type on %s--skipping\n",
680212397c6Schristos                             *argv);
681212397c6Schristos                     continue;
682212397c6Schristos                 }
683212397c6Schristos                 outname = malloc(len + 1);
684212397c6Schristos                 if (outname == NULL) {
685212397c6Schristos                     fprintf(stderr, "gun out of memory error--aborting\n");
686212397c6Schristos                     ret = 1;
687212397c6Schristos                     break;
688212397c6Schristos                 }
689212397c6Schristos                 memcpy(outname, *argv, len);
690212397c6Schristos                 outname[len] = 0;
691212397c6Schristos             }
692212397c6Schristos             ret = gunzip(&strm, *argv, outname, test);
693212397c6Schristos             if (outname != NULL) free(outname);
694212397c6Schristos             if (ret) break;
695212397c6Schristos         } while (argv++, --argc);
696212397c6Schristos     else
697212397c6Schristos         ret = gunzip(&strm, NULL, NULL, test);
698212397c6Schristos 
699212397c6Schristos     /* clean up */
700212397c6Schristos     inflateBackEnd(&strm);
701212397c6Schristos     return ret;
702212397c6Schristos }
703