| 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 } |