gun.c revision 1.1 1 1.1 christos /* $NetBSD: gun.c,v 1.1 2006/01/14 20:11:08 christos Exp $ */
2 1.1 christos
3 1.1 christos /* gun.c -- simple gunzip to give an example of the use of inflateBack()
4 1.1 christos * Copyright (C) 2003, 2005 Mark Adler
5 1.1 christos * For conditions of distribution and use, see copyright notice in zlib.h
6 1.1 christos Version 1.3 12 June 2005 Mark Adler */
7 1.1 christos
8 1.1 christos /* Version history:
9 1.1 christos 1.0 16 Feb 2003 First version for testing of inflateBack()
10 1.1 christos 1.1 21 Feb 2005 Decompress concatenated gzip streams
11 1.1 christos Remove use of "this" variable (C++ keyword)
12 1.1 christos Fix return value for in()
13 1.1 christos Improve allocation failure checking
14 1.1 christos Add typecasting for void * structures
15 1.1 christos Add -h option for command version and usage
16 1.1 christos Add a bunch of comments
17 1.1 christos 1.2 20 Mar 2005 Add Unix compress (LZW) decompression
18 1.1 christos Copy file attributes from input file to output file
19 1.1 christos 1.3 12 Jun 2005 Add casts for error messages [Oberhumer]
20 1.1 christos */
21 1.1 christos
22 1.1 christos /*
23 1.1 christos gun [ -t ] [ name ... ]
24 1.1 christos
25 1.1 christos decompresses the data in the named gzip files. If no arguments are given,
26 1.1 christos gun will decompress from stdin to stdout. The names must end in .gz, -gz,
27 1.1 christos .z, -z, _z, or .Z. The uncompressed data will be written to a file name
28 1.1 christos with the suffix stripped. On success, the original file is deleted. On
29 1.1 christos failure, the output file is deleted. For most failures, the command will
30 1.1 christos continue to process the remaining names on the command line. A memory
31 1.1 christos allocation failure will abort the command. If -t is specified, then the
32 1.1 christos listed files or stdin will be tested as gzip files for integrity (without
33 1.1 christos checking for a proper suffix), no output will be written, and no files
34 1.1 christos will be deleted.
35 1.1 christos
36 1.1 christos Like gzip, gun allows concatenated gzip streams and will decompress them,
37 1.1 christos writing all of the uncompressed data to the output. Unlike gzip, gun allows
38 1.1 christos an empty file on input, and will produce no error writing an empty output
39 1.1 christos file.
40 1.1 christos
41 1.1 christos gun will also decompress files made by Unix compress, which uses LZW
42 1.1 christos compression. These files are automatically detected by virtue of their
43 1.1 christos magic header bytes. Since the end of Unix compress stream is marked by the
44 1.1 christos end-of-file, they cannot be concantenated. If a Unix compress stream is
45 1.1 christos encountered in an input file, it is the last stream in that file.
46 1.1 christos
47 1.1 christos Like gunzip and uncompress, the file attributes of the orignal compressed
48 1.1 christos file are maintained in the final uncompressed file, to the extent that the
49 1.1 christos user permissions allow it.
50 1.1 christos
51 1.1 christos On my Mac OS X PowerPC G4, gun is almost twice as fast as gunzip (version
52 1.1 christos 1.2.4) is on the same file, when gun is linked with zlib 1.2.2. Also the
53 1.1 christos LZW decompression provided by gun is about twice as fast as the standard
54 1.1 christos Unix uncompress command.
55 1.1 christos */
56 1.1 christos
57 1.1 christos /* external functions and related types and constants */
58 1.1 christos #include <stdio.h> /* fprintf() */
59 1.1 christos #include <stdlib.h> /* malloc(), free() */
60 1.1 christos #include <string.h> /* strerror(), strcmp(), strlen(), memcpy() */
61 1.1 christos #include <errno.h> /* errno */
62 1.1 christos #include <fcntl.h> /* open() */
63 1.1 christos #include <unistd.h> /* read(), write(), close(), chown(), unlink() */
64 1.1 christos #include <sys/types.h>
65 1.1 christos #include <sys/stat.h> /* stat(), chmod() */
66 1.1 christos #include <utime.h> /* utime() */
67 1.1 christos #include "zlib.h" /* inflateBackInit(), inflateBack(), */
68 1.1 christos /* inflateBackEnd(), crc32() */
69 1.1 christos
70 1.1 christos /* function declaration */
71 1.1 christos #define local static
72 1.1 christos
73 1.1 christos /* buffer constants */
74 1.1 christos #define SIZE 32768U /* input and output buffer sizes */
75 1.1 christos #define PIECE 16384 /* limits i/o chunks for 16-bit int case */
76 1.1 christos
77 1.1 christos /* structure for infback() to pass to input function in() -- it maintains the
78 1.1 christos input file and a buffer of size SIZE */
79 1.1 christos struct ind {
80 1.1 christos int infile;
81 1.1 christos unsigned char *inbuf;
82 1.1 christos };
83 1.1 christos
84 1.1 christos /* Load input buffer, assumed to be empty, and return bytes loaded and a
85 1.1 christos pointer to them. read() is called until the buffer is full, or until it
86 1.1 christos returns end-of-file or error. Return 0 on error. */
87 1.1 christos local unsigned in(void *in_desc, unsigned char **buf)
88 1.1 christos {
89 1.1 christos int ret;
90 1.1 christos unsigned len;
91 1.1 christos unsigned char *next;
92 1.1 christos struct ind *me = (struct ind *)in_desc;
93 1.1 christos
94 1.1 christos next = me->inbuf;
95 1.1 christos *buf = next;
96 1.1 christos len = 0;
97 1.1 christos do {
98 1.1 christos ret = PIECE;
99 1.1 christos if ((unsigned)ret > SIZE - len)
100 1.1 christos ret = (int)(SIZE - len);
101 1.1 christos ret = (int)read(me->infile, next, ret);
102 1.1 christos if (ret == -1) {
103 1.1 christos len = 0;
104 1.1 christos break;
105 1.1 christos }
106 1.1 christos next += ret;
107 1.1 christos len += ret;
108 1.1 christos } while (ret != 0 && len < SIZE);
109 1.1 christos return len;
110 1.1 christos }
111 1.1 christos
112 1.1 christos /* structure for infback() to pass to output function out() -- it maintains the
113 1.1 christos output file, a running CRC-32 check on the output and the total number of
114 1.1 christos bytes output, both for checking against the gzip trailer. (The length in
115 1.1 christos the gzip trailer is stored modulo 2^32, so it's ok if a long is 32 bits and
116 1.1 christos the output is greater than 4 GB.) */
117 1.1 christos struct outd {
118 1.1 christos int outfile;
119 1.1 christos int check; /* true if checking crc and total */
120 1.1 christos unsigned long crc;
121 1.1 christos unsigned long total;
122 1.1 christos };
123 1.1 christos
124 1.1 christos /* Write output buffer and update the CRC-32 and total bytes written. write()
125 1.1 christos is called until all of the output is written or an error is encountered.
126 1.1 christos On success out() returns 0. For a write failure, out() returns 1. If the
127 1.1 christos output file descriptor is -1, then nothing is written.
128 1.1 christos */
129 1.1 christos local int out(void *out_desc, unsigned char *buf, unsigned len)
130 1.1 christos {
131 1.1 christos int ret;
132 1.1 christos struct outd *me = (struct outd *)out_desc;
133 1.1 christos
134 1.1 christos if (me->check) {
135 1.1 christos me->crc = crc32(me->crc, buf, len);
136 1.1 christos me->total += len;
137 1.1 christos }
138 1.1 christos if (me->outfile != -1)
139 1.1 christos do {
140 1.1 christos ret = PIECE;
141 1.1 christos if ((unsigned)ret > len)
142 1.1 christos ret = (int)len;
143 1.1 christos ret = (int)write(me->outfile, buf, ret);
144 1.1 christos if (ret == -1)
145 1.1 christos return 1;
146 1.1 christos buf += ret;
147 1.1 christos len -= ret;
148 1.1 christos } while (len != 0);
149 1.1 christos return 0;
150 1.1 christos }
151 1.1 christos
152 1.1 christos /* next input byte macro for use inside lunpipe() and gunpipe() */
153 1.1 christos #define NEXT() (have ? 0 : (have = in(indp, &next)), \
154 1.1 christos last = have ? (have--, (int)(*next++)) : -1)
155 1.1 christos
156 1.1 christos /* memory for gunpipe() and lunpipe() --
157 1.1 christos the first 256 entries of prefix[] and suffix[] are never used, could
158 1.1 christos have offset the index, but it's faster to waste the memory */
159 1.1 christos unsigned char inbuf[SIZE]; /* input buffer */
160 1.1 christos unsigned char outbuf[SIZE]; /* output buffer */
161 1.1 christos unsigned short prefix[65536]; /* index to LZW prefix string */
162 1.1 christos unsigned char suffix[65536]; /* one-character LZW suffix */
163 1.1 christos unsigned char match[65280 + 2]; /* buffer for reversed match or gzip
164 1.1 christos 32K sliding window */
165 1.1 christos
166 1.1 christos /* throw out what's left in the current bits byte buffer (this is a vestigial
167 1.1 christos aspect of the compressed data format derived from an implementation that
168 1.1 christos made use of a special VAX machine instruction!) */
169 1.1 christos #define FLUSHCODE() \
170 1.1 christos do { \
171 1.1 christos left = 0; \
172 1.1 christos rem = 0; \
173 1.1 christos if (chunk > have) { \
174 1.1 christos chunk -= have; \
175 1.1 christos have = 0; \
176 1.1 christos if (NEXT() == -1) \
177 1.1 christos break; \
178 1.1 christos chunk--; \
179 1.1 christos if (chunk > have) { \
180 1.1 christos chunk = have = 0; \
181 1.1 christos break; \
182 1.1 christos } \
183 1.1 christos } \
184 1.1 christos have -= chunk; \
185 1.1 christos next += chunk; \
186 1.1 christos chunk = 0; \
187 1.1 christos } while (0)
188 1.1 christos
189 1.1 christos /* Decompress a compress (LZW) file from indp to outfile. The compress magic
190 1.1 christos header (two bytes) has already been read and verified. There are have bytes
191 1.1 christos of buffered input at next. strm is used for passing error information back
192 1.1 christos to gunpipe().
193 1.1 christos
194 1.1 christos lunpipe() will return Z_OK on success, Z_BUF_ERROR for an unexpected end of
195 1.1 christos file, read error, or write error (a write error indicated by strm->next_in
196 1.1 christos not equal to Z_NULL), or Z_DATA_ERROR for invalid input.
197 1.1 christos */
198 1.1 christos local int lunpipe(unsigned have, unsigned char *next, struct ind *indp,
199 1.1 christos int outfile, z_stream *strm)
200 1.1 christos {
201 1.1 christos int last; /* last byte read by NEXT(), or -1 if EOF */
202 1.1 christos int chunk; /* bytes left in current chunk */
203 1.1 christos int left; /* bits left in rem */
204 1.1 christos unsigned rem; /* unused bits from input */
205 1.1 christos int bits; /* current bits per code */
206 1.1 christos unsigned code; /* code, table traversal index */
207 1.1 christos unsigned mask; /* mask for current bits codes */
208 1.1 christos int max; /* maximum bits per code for this stream */
209 1.1 christos int flags; /* compress flags, then block compress flag */
210 1.1 christos unsigned end; /* last valid entry in prefix/suffix tables */
211 1.1 christos unsigned temp; /* current code */
212 1.1 christos unsigned prev; /* previous code */
213 1.1 christos unsigned final; /* last character written for previous code */
214 1.1 christos unsigned stack; /* next position for reversed string */
215 1.1 christos unsigned outcnt; /* bytes in output buffer */
216 1.1 christos struct outd outd; /* output structure */
217 1.1 christos
218 1.1 christos /* set up output */
219 1.1 christos outd.outfile = outfile;
220 1.1 christos outd.check = 0;
221 1.1 christos
222 1.1 christos /* process remainder of compress header -- a flags byte */
223 1.1 christos flags = NEXT();
224 1.1 christos if (last == -1)
225 1.1 christos return Z_BUF_ERROR;
226 1.1 christos if (flags & 0x60) {
227 1.1 christos strm->msg = (char *)"unknown lzw flags set";
228 1.1 christos return Z_DATA_ERROR;
229 1.1 christos }
230 1.1 christos max = flags & 0x1f;
231 1.1 christos if (max < 9 || max > 16) {
232 1.1 christos strm->msg = (char *)"lzw bits out of range";
233 1.1 christos return Z_DATA_ERROR;
234 1.1 christos }
235 1.1 christos if (max == 9) /* 9 doesn't really mean 9 */
236 1.1 christos max = 10;
237 1.1 christos flags &= 0x80; /* true if block compress */
238 1.1 christos
239 1.1 christos /* clear table */
240 1.1 christos bits = 9;
241 1.1 christos mask = 0x1ff;
242 1.1 christos end = flags ? 256 : 255;
243 1.1 christos
244 1.1 christos /* set up: get first 9-bit code, which is the first decompressed byte, but
245 1.1 christos don't create a table entry until the next code */
246 1.1 christos if (NEXT() == -1) /* no compressed data is ok */
247 1.1 christos return Z_OK;
248 1.1 christos final = prev = (unsigned)last; /* low 8 bits of code */
249 1.1 christos if (NEXT() == -1) /* missing a bit */
250 1.1 christos return Z_BUF_ERROR;
251 1.1 christos if (last & 1) { /* code must be < 256 */
252 1.1 christos strm->msg = (char *)"invalid lzw code";
253 1.1 christos return Z_DATA_ERROR;
254 1.1 christos }
255 1.1 christos rem = (unsigned)last >> 1; /* remaining 7 bits */
256 1.1 christos left = 7;
257 1.1 christos chunk = bits - 2; /* 7 bytes left in this chunk */
258 1.1 christos outbuf[0] = (unsigned char)final; /* write first decompressed byte */
259 1.1 christos outcnt = 1;
260 1.1 christos
261 1.1 christos /* decode codes */
262 1.1 christos stack = 0;
263 1.1 christos for (;;) {
264 1.1 christos /* if the table will be full after this, increment the code size */
265 1.1 christos if (end >= mask && bits < max) {
266 1.1 christos FLUSHCODE();
267 1.1 christos bits++;
268 1.1 christos mask <<= 1;
269 1.1 christos mask++;
270 1.1 christos }
271 1.1 christos
272 1.1 christos /* get a code of length bits */
273 1.1 christos if (chunk == 0) /* decrement chunk modulo bits */
274 1.1 christos chunk = bits;
275 1.1 christos code = rem; /* low bits of code */
276 1.1 christos if (NEXT() == -1) { /* EOF is end of compressed data */
277 1.1 christos /* write remaining buffered output */
278 1.1 christos if (outcnt && out(&outd, outbuf, outcnt)) {
279 1.1 christos strm->next_in = outbuf; /* signal write error */
280 1.1 christos return Z_BUF_ERROR;
281 1.1 christos }
282 1.1 christos return Z_OK;
283 1.1 christos }
284 1.1 christos code += (unsigned)last << left; /* middle (or high) bits of code */
285 1.1 christos left += 8;
286 1.1 christos chunk--;
287 1.1 christos if (bits > left) { /* need more bits */
288 1.1 christos if (NEXT() == -1) /* can't end in middle of code */
289 1.1 christos return Z_BUF_ERROR;
290 1.1 christos code += (unsigned)last << left; /* high bits of code */
291 1.1 christos left += 8;
292 1.1 christos chunk--;
293 1.1 christos }
294 1.1 christos code &= mask; /* mask to current code length */
295 1.1 christos left -= bits; /* number of unused bits */
296 1.1 christos rem = (unsigned)last >> (8 - left); /* unused bits from last byte */
297 1.1 christos
298 1.1 christos /* process clear code (256) */
299 1.1 christos if (code == 256 && flags) {
300 1.1 christos FLUSHCODE();
301 1.1 christos bits = 9; /* initialize bits and mask */
302 1.1 christos mask = 0x1ff;
303 1.1 christos end = 255; /* empty table */
304 1.1 christos continue; /* get next code */
305 1.1 christos }
306 1.1 christos
307 1.1 christos /* special code to reuse last match */
308 1.1 christos temp = code; /* save the current code */
309 1.1 christos if (code > end) {
310 1.1 christos /* Be picky on the allowed code here, and make sure that the code
311 1.1 christos we drop through (prev) will be a valid index so that random
312 1.1 christos input does not cause an exception. The code != end + 1 check is
313 1.1 christos empirically derived, and not checked in the original uncompress
314 1.1 christos code. If this ever causes a problem, that check could be safely
315 1.1 christos removed. Leaving this check in greatly improves gun's ability
316 1.1 christos to detect random or corrupted input after a compress header.
317 1.1 christos In any case, the prev > end check must be retained. */
318 1.1 christos if (code != end + 1 || prev > end) {
319 1.1 christos strm->msg = (char *)"invalid lzw code";
320 1.1 christos return Z_DATA_ERROR;
321 1.1 christos }
322 1.1 christos match[stack++] = (unsigned char)final;
323 1.1 christos code = prev;
324 1.1 christos }
325 1.1 christos
326 1.1 christos /* walk through linked list to generate output in reverse order */
327 1.1 christos while (code >= 256) {
328 1.1 christos match[stack++] = suffix[code];
329 1.1 christos code = prefix[code];
330 1.1 christos }
331 1.1 christos match[stack++] = (unsigned char)code;
332 1.1 christos final = code;
333 1.1 christos
334 1.1 christos /* link new table entry */
335 1.1 christos if (end < mask) {
336 1.1 christos end++;
337 1.1 christos prefix[end] = (unsigned short)prev;
338 1.1 christos suffix[end] = (unsigned char)final;
339 1.1 christos }
340 1.1 christos
341 1.1 christos /* set previous code for next iteration */
342 1.1 christos prev = temp;
343 1.1 christos
344 1.1 christos /* write output in forward order */
345 1.1 christos while (stack > SIZE - outcnt) {
346 1.1 christos while (outcnt < SIZE)
347 1.1 christos outbuf[outcnt++] = match[--stack];
348 1.1 christos if (out(&outd, outbuf, outcnt)) {
349 1.1 christos strm->next_in = outbuf; /* signal write error */
350 1.1 christos return Z_BUF_ERROR;
351 1.1 christos }
352 1.1 christos outcnt = 0;
353 1.1 christos }
354 1.1 christos do {
355 1.1 christos outbuf[outcnt++] = match[--stack];
356 1.1 christos } while (stack);
357 1.1 christos
358 1.1 christos /* loop for next code with final and prev as the last match, rem and
359 1.1 christos left provide the first 0..7 bits of the next code, end is the last
360 1.1 christos valid table entry */
361 1.1 christos }
362 1.1 christos }
363 1.1 christos
364 1.1 christos /* Decompress a gzip file from infile to outfile. strm is assumed to have been
365 1.1 christos successfully initialized with inflateBackInit(). The input file may consist
366 1.1 christos of a series of gzip streams, in which case all of them will be decompressed
367 1.1 christos to the output file. If outfile is -1, then the gzip stream(s) integrity is
368 1.1 christos checked and nothing is written.
369 1.1 christos
370 1.1 christos The return value is a zlib error code: Z_MEM_ERROR if out of memory,
371 1.1 christos Z_DATA_ERROR if the header or the compressed data is invalid, or if the
372 1.1 christos trailer CRC-32 check or length doesn't match, Z_BUF_ERROR if the input ends
373 1.1 christos prematurely or a write error occurs, or Z_ERRNO if junk (not a another gzip
374 1.1 christos stream) follows a valid gzip stream.
375 1.1 christos */
376 1.1 christos local int gunpipe(z_stream *strm, int infile, int outfile)
377 1.1 christos {
378 1.1 christos int ret, first, last;
379 1.1 christos unsigned have, flags, len;
380 1.1 christos unsigned char *next;
381 1.1 christos struct ind ind, *indp;
382 1.1 christos struct outd outd;
383 1.1 christos
384 1.1 christos /* setup input buffer */
385 1.1 christos ind.infile = infile;
386 1.1 christos ind.inbuf = inbuf;
387 1.1 christos indp = &ind;
388 1.1 christos
389 1.1 christos /* decompress concatenated gzip streams */
390 1.1 christos have = 0; /* no input data read in yet */
391 1.1 christos first = 1; /* looking for first gzip header */
392 1.1 christos strm->next_in = Z_NULL; /* so Z_BUF_ERROR means EOF */
393 1.1 christos for (;;) {
394 1.1 christos /* look for the two magic header bytes for a gzip stream */
395 1.1 christos if (NEXT() == -1) {
396 1.1 christos ret = Z_OK;
397 1.1 christos break; /* empty gzip stream is ok */
398 1.1 christos }
399 1.1 christos if (last != 31 || (NEXT() != 139 && last != 157)) {
400 1.1 christos strm->msg = (char *)"incorrect header check";
401 1.1 christos ret = first ? Z_DATA_ERROR : Z_ERRNO;
402 1.1 christos break; /* not a gzip or compress header */
403 1.1 christos }
404 1.1 christos first = 0; /* next non-header is junk */
405 1.1 christos
406 1.1 christos /* process a compress (LZW) file -- can't be concatenated after this */
407 1.1 christos if (last == 157) {
408 1.1 christos ret = lunpipe(have, next, indp, outfile, strm);
409 1.1 christos break;
410 1.1 christos }
411 1.1 christos
412 1.1 christos /* process remainder of gzip header */
413 1.1 christos ret = Z_BUF_ERROR;
414 1.1 christos if (NEXT() != 8) { /* only deflate method allowed */
415 1.1 christos if (last == -1) break;
416 1.1 christos strm->msg = (char *)"unknown compression method";
417 1.1 christos ret = Z_DATA_ERROR;
418 1.1 christos break;
419 1.1 christos }
420 1.1 christos flags = NEXT(); /* header flags */
421 1.1 christos NEXT(); /* discard mod time, xflgs, os */
422 1.1 christos NEXT();
423 1.1 christos NEXT();
424 1.1 christos NEXT();
425 1.1 christos NEXT();
426 1.1 christos NEXT();
427 1.1 christos if (last == -1) break;
428 1.1 christos if (flags & 0xe0) {
429 1.1 christos strm->msg = (char *)"unknown header flags set";
430 1.1 christos ret = Z_DATA_ERROR;
431 1.1 christos break;
432 1.1 christos }
433 1.1 christos if (flags & 4) { /* extra field */
434 1.1 christos len = NEXT();
435 1.1 christos len += (unsigned)(NEXT()) << 8;
436 1.1 christos if (last == -1) break;
437 1.1 christos while (len > have) {
438 1.1 christos len -= have;
439 1.1 christos have = 0;
440 1.1 christos if (NEXT() == -1) break;
441 1.1 christos len--;
442 1.1 christos }
443 1.1 christos if (last == -1) break;
444 1.1 christos have -= len;
445 1.1 christos next += len;
446 1.1 christos }
447 1.1 christos if (flags & 8) /* file name */
448 1.1 christos while (NEXT() != 0 && last != -1)
449 1.1 christos ;
450 1.1 christos if (flags & 16) /* comment */
451 1.1 christos while (NEXT() != 0 && last != -1)
452 1.1 christos ;
453 1.1 christos if (flags & 2) { /* header crc */
454 1.1 christos NEXT();
455 1.1 christos NEXT();
456 1.1 christos }
457 1.1 christos if (last == -1) break;
458 1.1 christos
459 1.1 christos /* set up output */
460 1.1 christos outd.outfile = outfile;
461 1.1 christos outd.check = 1;
462 1.1 christos outd.crc = crc32(0L, Z_NULL, 0);
463 1.1 christos outd.total = 0;
464 1.1 christos
465 1.1 christos /* decompress data to output */
466 1.1 christos strm->next_in = next;
467 1.1 christos strm->avail_in = have;
468 1.1 christos ret = inflateBack(strm, in, indp, out, &outd);
469 1.1 christos if (ret != Z_STREAM_END) break;
470 1.1 christos next = strm->next_in;
471 1.1 christos have = strm->avail_in;
472 1.1 christos strm->next_in = Z_NULL; /* so Z_BUF_ERROR means EOF */
473 1.1 christos
474 1.1 christos /* check trailer */
475 1.1 christos ret = Z_BUF_ERROR;
476 1.1 christos if (NEXT() != (outd.crc & 0xff) ||
477 1.1 christos NEXT() != ((outd.crc >> 8) & 0xff) ||
478 1.1 christos NEXT() != ((outd.crc >> 16) & 0xff) ||
479 1.1 christos NEXT() != ((outd.crc >> 24) & 0xff)) {
480 1.1 christos /* crc error */
481 1.1 christos if (last != -1) {
482 1.1 christos strm->msg = (char *)"incorrect data check";
483 1.1 christos ret = Z_DATA_ERROR;
484 1.1 christos }
485 1.1 christos break;
486 1.1 christos }
487 1.1 christos if (NEXT() != (outd.total & 0xff) ||
488 1.1 christos NEXT() != ((outd.total >> 8) & 0xff) ||
489 1.1 christos NEXT() != ((outd.total >> 16) & 0xff) ||
490 1.1 christos NEXT() != ((outd.total >> 24) & 0xff)) {
491 1.1 christos /* length error */
492 1.1 christos if (last != -1) {
493 1.1 christos strm->msg = (char *)"incorrect length check";
494 1.1 christos ret = Z_DATA_ERROR;
495 1.1 christos }
496 1.1 christos break;
497 1.1 christos }
498 1.1 christos
499 1.1 christos /* go back and look for another gzip stream */
500 1.1 christos }
501 1.1 christos
502 1.1 christos /* clean up and return */
503 1.1 christos return ret;
504 1.1 christos }
505 1.1 christos
506 1.1 christos /* Copy file attributes, from -> to, as best we can. This is best effort, so
507 1.1 christos no errors are reported. The mode bits, including suid, sgid, and the sticky
508 1.1 christos bit are copied (if allowed), the owner's user id and group id are copied
509 1.1 christos (again if allowed), and the access and modify times are copied. */
510 1.1 christos local void copymeta(char *from, char *to)
511 1.1 christos {
512 1.1 christos struct stat was;
513 1.1 christos struct utimbuf when;
514 1.1 christos
515 1.1 christos /* get all of from's Unix meta data, return if not a regular file */
516 1.1 christos if (stat(from, &was) != 0 || (was.st_mode & S_IFMT) != S_IFREG)
517 1.1 christos return;
518 1.1 christos
519 1.1 christos /* set to's mode bits, ignore errors */
520 1.1 christos (void)chmod(to, was.st_mode & 07777);
521 1.1 christos
522 1.1 christos /* copy owner's user and group, ignore errors */
523 1.1 christos (void)chown(to, was.st_uid, was.st_gid);
524 1.1 christos
525 1.1 christos /* copy access and modify times, ignore errors */
526 1.1 christos when.actime = was.st_atime;
527 1.1 christos when.modtime = was.st_mtime;
528 1.1 christos (void)utime(to, &when);
529 1.1 christos }
530 1.1 christos
531 1.1 christos /* Decompress the file inname to the file outnname, of if test is true, just
532 1.1 christos decompress without writing and check the gzip trailer for integrity. If
533 1.1 christos inname is NULL or an empty string, read from stdin. If outname is NULL or
534 1.1 christos an empty string, write to stdout. strm is a pre-initialized inflateBack
535 1.1 christos structure. When appropriate, copy the file attributes from inname to
536 1.1 christos outname.
537 1.1 christos
538 1.1 christos gunzip() returns 1 if there is an out-of-memory error or an unexpected
539 1.1 christos return code from gunpipe(). Otherwise it returns 0.
540 1.1 christos */
541 1.1 christos local int gunzip(z_stream *strm, char *inname, char *outname, int test)
542 1.1 christos {
543 1.1 christos int ret;
544 1.1 christos int infile, outfile;
545 1.1 christos
546 1.1 christos /* open files */
547 1.1 christos if (inname == NULL || *inname == 0) {
548 1.1 christos inname = "-";
549 1.1 christos infile = 0; /* stdin */
550 1.1 christos }
551 1.1 christos else {
552 1.1 christos infile = open(inname, O_RDONLY, 0);
553 1.1 christos if (infile == -1) {
554 1.1 christos fprintf(stderr, "gun cannot open %s\n", inname);
555 1.1 christos return 0;
556 1.1 christos }
557 1.1 christos }
558 1.1 christos if (test)
559 1.1 christos outfile = -1;
560 1.1 christos else if (outname == NULL || *outname == 0) {
561 1.1 christos outname = "-";
562 1.1 christos outfile = 1; /* stdout */
563 1.1 christos }
564 1.1 christos else {
565 1.1 christos outfile = open(outname, O_CREAT | O_TRUNC | O_WRONLY, 0666);
566 1.1 christos if (outfile == -1) {
567 1.1 christos close(infile);
568 1.1 christos fprintf(stderr, "gun cannot create %s\n", outname);
569 1.1 christos return 0;
570 1.1 christos }
571 1.1 christos }
572 1.1 christos errno = 0;
573 1.1 christos
574 1.1 christos /* decompress */
575 1.1 christos ret = gunpipe(strm, infile, outfile);
576 1.1 christos if (outfile > 2) close(outfile);
577 1.1 christos if (infile > 2) close(infile);
578 1.1 christos
579 1.1 christos /* interpret result */
580 1.1 christos switch (ret) {
581 1.1 christos case Z_OK:
582 1.1 christos case Z_ERRNO:
583 1.1 christos if (infile > 2 && outfile > 2) {
584 1.1 christos copymeta(inname, outname); /* copy attributes */
585 1.1 christos unlink(inname);
586 1.1 christos }
587 1.1 christos if (ret == Z_ERRNO)
588 1.1 christos fprintf(stderr, "gun warning: trailing garbage ignored in %s\n",
589 1.1 christos inname);
590 1.1 christos break;
591 1.1 christos case Z_DATA_ERROR:
592 1.1 christos if (outfile > 2) unlink(outname);
593 1.1 christos fprintf(stderr, "gun data error on %s: %s\n", inname, strm->msg);
594 1.1 christos break;
595 1.1 christos case Z_MEM_ERROR:
596 1.1 christos if (outfile > 2) unlink(outname);
597 1.1 christos fprintf(stderr, "gun out of memory error--aborting\n");
598 1.1 christos return 1;
599 1.1 christos case Z_BUF_ERROR:
600 1.1 christos if (outfile > 2) unlink(outname);
601 1.1 christos if (strm->next_in != Z_NULL) {
602 1.1 christos fprintf(stderr, "gun write error on %s: %s\n",
603 1.1 christos outname, strerror(errno));
604 1.1 christos }
605 1.1 christos else if (errno) {
606 1.1 christos fprintf(stderr, "gun read error on %s: %s\n",
607 1.1 christos inname, strerror(errno));
608 1.1 christos }
609 1.1 christos else {
610 1.1 christos fprintf(stderr, "gun unexpected end of file on %s\n",
611 1.1 christos inname);
612 1.1 christos }
613 1.1 christos break;
614 1.1 christos default:
615 1.1 christos if (outfile > 2) unlink(outname);
616 1.1 christos fprintf(stderr, "gun internal error--aborting\n");
617 1.1 christos return 1;
618 1.1 christos }
619 1.1 christos return 0;
620 1.1 christos }
621 1.1 christos
622 1.1 christos /* Process the gun command line arguments. See the command syntax near the
623 1.1 christos beginning of this source file. */
624 1.1 christos int main(int argc, char **argv)
625 1.1 christos {
626 1.1 christos int ret, len, test;
627 1.1 christos char *outname;
628 1.1 christos unsigned char *window;
629 1.1 christos z_stream strm;
630 1.1 christos
631 1.1 christos /* initialize inflateBack state for repeated use */
632 1.1 christos window = match; /* reuse LZW match buffer */
633 1.1 christos strm.zalloc = Z_NULL;
634 1.1 christos strm.zfree = Z_NULL;
635 1.1 christos strm.opaque = Z_NULL;
636 1.1 christos ret = inflateBackInit(&strm, 15, window);
637 1.1 christos if (ret != Z_OK) {
638 1.1 christos fprintf(stderr, "gun out of memory error--aborting\n");
639 1.1 christos return 1;
640 1.1 christos }
641 1.1 christos
642 1.1 christos /* decompress each file to the same name with the suffix removed */
643 1.1 christos argc--;
644 1.1 christos argv++;
645 1.1 christos test = 0;
646 1.1 christos if (argc && strcmp(*argv, "-h") == 0) {
647 1.1 christos fprintf(stderr, "gun 1.3 (12 Jun 2005)\n");
648 1.1 christos fprintf(stderr, "Copyright (c) 2005 Mark Adler\n");
649 1.1 christos fprintf(stderr, "usage: gun [-t] [file1.gz [file2.Z ...]]\n");
650 1.1 christos return 0;
651 1.1 christos }
652 1.1 christos if (argc && strcmp(*argv, "-t") == 0) {
653 1.1 christos test = 1;
654 1.1 christos argc--;
655 1.1 christos argv++;
656 1.1 christos }
657 1.1 christos if (argc)
658 1.1 christos do {
659 1.1 christos if (test)
660 1.1 christos outname = NULL;
661 1.1 christos else {
662 1.1 christos len = (int)strlen(*argv);
663 1.1 christos if (strcmp(*argv + len - 3, ".gz") == 0 ||
664 1.1 christos strcmp(*argv + len - 3, "-gz") == 0)
665 1.1 christos len -= 3;
666 1.1 christos else if (strcmp(*argv + len - 2, ".z") == 0 ||
667 1.1 christos strcmp(*argv + len - 2, "-z") == 0 ||
668 1.1 christos strcmp(*argv + len - 2, "_z") == 0 ||
669 1.1 christos strcmp(*argv + len - 2, ".Z") == 0)
670 1.1 christos len -= 2;
671 1.1 christos else {
672 1.1 christos fprintf(stderr, "gun error: no gz type on %s--skipping\n",
673 1.1 christos *argv);
674 1.1 christos continue;
675 1.1 christos }
676 1.1 christos outname = malloc(len + 1);
677 1.1 christos if (outname == NULL) {
678 1.1 christos fprintf(stderr, "gun out of memory error--aborting\n");
679 1.1 christos ret = 1;
680 1.1 christos break;
681 1.1 christos }
682 1.1 christos memcpy(outname, *argv, len);
683 1.1 christos outname[len] = 0;
684 1.1 christos }
685 1.1 christos ret = gunzip(&strm, *argv, outname, test);
686 1.1 christos if (outname != NULL) free(outname);
687 1.1 christos if (ret) break;
688 1.1 christos } while (argv++, --argc);
689 1.1 christos else
690 1.1 christos ret = gunzip(&strm, NULL, NULL, test);
691 1.1 christos
692 1.1 christos /* clean up */
693 1.1 christos inflateBackEnd(&strm);
694 1.1 christos return ret;
695 1.1 christos }
696