annotate src/zlib-1.2.7/examples/gun.c @ 169:223a55898ab9 tip default

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