annotate src/zlib-1.2.7/examples/gun.c @ 83:ae30d91d2ffe

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