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