xref: /netbsd-src/external/gpl3/binutils/dist/zlib/examples/gun.c (revision fc4f42693f9b1c31f39f9cf50af1bf2010325808)
19573673dSchristos /* gun.c -- simple gunzip to give an example of the use of inflateBack()
28cbf5cb7Schristos  * Copyright (C) 2003, 2005, 2008, 2010, 2012 Mark Adler
39573673dSchristos  * For conditions of distribution and use, see copyright notice in zlib.h
48cbf5cb7Schristos    Version 1.7  12 August 2012  Mark Adler */
59573673dSchristos 
69573673dSchristos /* Version history:
79573673dSchristos    1.0  16 Feb 2003  First version for testing of inflateBack()
89573673dSchristos    1.1  21 Feb 2005  Decompress concatenated gzip streams
99573673dSchristos                      Remove use of "this" variable (C++ keyword)
109573673dSchristos                      Fix return value for in()
119573673dSchristos                      Improve allocation failure checking
129573673dSchristos                      Add typecasting for void * structures
139573673dSchristos                      Add -h option for command version and usage
149573673dSchristos                      Add a bunch of comments
159573673dSchristos    1.2  20 Mar 2005  Add Unix compress (LZW) decompression
169573673dSchristos                      Copy file attributes from input file to output file
179573673dSchristos    1.3  12 Jun 2005  Add casts for error messages [Oberhumer]
189573673dSchristos    1.4   8 Dec 2006  LZW decompression speed improvements
199573673dSchristos    1.5   9 Feb 2008  Avoid warning in latest version of gcc
209573673dSchristos    1.6  17 Jan 2010  Avoid signed/unsigned comparison warnings
218cbf5cb7Schristos    1.7  12 Aug 2012  Update for z_const usage in zlib 1.2.8
229573673dSchristos  */
239573673dSchristos 
249573673dSchristos /*
259573673dSchristos    gun [ -t ] [ name ... ]
269573673dSchristos 
279573673dSchristos    decompresses the data in the named gzip files.  If no arguments are given,
289573673dSchristos    gun will decompress from stdin to stdout.  The names must end in .gz, -gz,
299573673dSchristos    .z, -z, _z, or .Z.  The uncompressed data will be written to a file name
309573673dSchristos    with the suffix stripped.  On success, the original file is deleted.  On
319573673dSchristos    failure, the output file is deleted.  For most failures, the command will
329573673dSchristos    continue to process the remaining names on the command line.  A memory
339573673dSchristos    allocation failure will abort the command.  If -t is specified, then the
349573673dSchristos    listed files or stdin will be tested as gzip files for integrity (without
359573673dSchristos    checking for a proper suffix), no output will be written, and no files
369573673dSchristos    will be deleted.
379573673dSchristos 
389573673dSchristos    Like gzip, gun allows concatenated gzip streams and will decompress them,
399573673dSchristos    writing all of the uncompressed data to the output.  Unlike gzip, gun allows
409573673dSchristos    an empty file on input, and will produce no error writing an empty output
419573673dSchristos    file.
429573673dSchristos 
439573673dSchristos    gun will also decompress files made by Unix compress, which uses LZW
449573673dSchristos    compression.  These files are automatically detected by virtue of their
459573673dSchristos    magic header bytes.  Since the end of Unix compress stream is marked by the
469573673dSchristos    end-of-file, they cannot be concantenated.  If a Unix compress stream is
479573673dSchristos    encountered in an input file, it is the last stream in that file.
489573673dSchristos 
49*fc4f4269Schristos    Like gunzip and uncompress, the file attributes of the original compressed
509573673dSchristos    file are maintained in the final uncompressed file, to the extent that the
519573673dSchristos    user permissions allow it.
529573673dSchristos 
539573673dSchristos    On my Mac OS X PowerPC G4, gun is almost twice as fast as gunzip (version
549573673dSchristos    1.2.4) is on the same file, when gun is linked with zlib 1.2.2.  Also the
559573673dSchristos    LZW decompression provided by gun is about twice as fast as the standard
569573673dSchristos    Unix uncompress command.
579573673dSchristos  */
589573673dSchristos 
599573673dSchristos /* external functions and related types and constants */
609573673dSchristos #include <stdio.h>          /* fprintf() */
619573673dSchristos #include <stdlib.h>         /* malloc(), free() */
629573673dSchristos #include <string.h>         /* strerror(), strcmp(), strlen(), memcpy() */
639573673dSchristos #include <errno.h>          /* errno */
649573673dSchristos #include <fcntl.h>          /* open() */
659573673dSchristos #include <unistd.h>         /* read(), write(), close(), chown(), unlink() */
669573673dSchristos #include <sys/types.h>
679573673dSchristos #include <sys/stat.h>       /* stat(), chmod() */
689573673dSchristos #include <utime.h>          /* utime() */
699573673dSchristos #include "zlib.h"           /* inflateBackInit(), inflateBack(), */
709573673dSchristos                             /* inflateBackEnd(), crc32() */
719573673dSchristos 
729573673dSchristos /* function declaration */
739573673dSchristos #define local static
749573673dSchristos 
759573673dSchristos /* buffer constants */
769573673dSchristos #define SIZE 32768U         /* input and output buffer sizes */
779573673dSchristos #define PIECE 16384         /* limits i/o chunks for 16-bit int case */
789573673dSchristos 
799573673dSchristos /* structure for infback() to pass to input function in() -- it maintains the
809573673dSchristos    input file and a buffer of size SIZE */
819573673dSchristos struct ind {
829573673dSchristos     int infile;
839573673dSchristos     unsigned char *inbuf;
849573673dSchristos };
859573673dSchristos 
869573673dSchristos /* Load input buffer, assumed to be empty, and return bytes loaded and a
879573673dSchristos    pointer to them.  read() is called until the buffer is full, or until it
889573673dSchristos    returns end-of-file or error.  Return 0 on error. */
in(void * in_desc,z_const unsigned char ** buf)898cbf5cb7Schristos local unsigned in(void *in_desc, z_const unsigned char **buf)
909573673dSchristos {
919573673dSchristos     int ret;
929573673dSchristos     unsigned len;
939573673dSchristos     unsigned char *next;
949573673dSchristos     struct ind *me = (struct ind *)in_desc;
959573673dSchristos 
969573673dSchristos     next = me->inbuf;
979573673dSchristos     *buf = next;
989573673dSchristos     len = 0;
999573673dSchristos     do {
1009573673dSchristos         ret = PIECE;
1019573673dSchristos         if ((unsigned)ret > SIZE - len)
1029573673dSchristos             ret = (int)(SIZE - len);
1039573673dSchristos         ret = (int)read(me->infile, next, ret);
1049573673dSchristos         if (ret == -1) {
1059573673dSchristos             len = 0;
1069573673dSchristos             break;
1079573673dSchristos         }
1089573673dSchristos         next += ret;
1099573673dSchristos         len += ret;
1109573673dSchristos     } while (ret != 0 && len < SIZE);
1119573673dSchristos     return len;
1129573673dSchristos }
1139573673dSchristos 
1149573673dSchristos /* structure for infback() to pass to output function out() -- it maintains the
1159573673dSchristos    output file, a running CRC-32 check on the output and the total number of
1169573673dSchristos    bytes output, both for checking against the gzip trailer.  (The length in
1179573673dSchristos    the gzip trailer is stored modulo 2^32, so it's ok if a long is 32 bits and
1189573673dSchristos    the output is greater than 4 GB.) */
1199573673dSchristos struct outd {
1209573673dSchristos     int outfile;
1219573673dSchristos     int check;                  /* true if checking crc and total */
1229573673dSchristos     unsigned long crc;
1239573673dSchristos     unsigned long total;
1249573673dSchristos };
1259573673dSchristos 
1269573673dSchristos /* Write output buffer and update the CRC-32 and total bytes written.  write()
1279573673dSchristos    is called until all of the output is written or an error is encountered.
1289573673dSchristos    On success out() returns 0.  For a write failure, out() returns 1.  If the
1299573673dSchristos    output file descriptor is -1, then nothing is written.
1309573673dSchristos  */
out(void * out_desc,unsigned char * buf,unsigned len)1319573673dSchristos local int out(void *out_desc, unsigned char *buf, unsigned len)
1329573673dSchristos {
1339573673dSchristos     int ret;
1349573673dSchristos     struct outd *me = (struct outd *)out_desc;
1359573673dSchristos 
1369573673dSchristos     if (me->check) {
1379573673dSchristos         me->crc = crc32(me->crc, buf, len);
1389573673dSchristos         me->total += len;
1399573673dSchristos     }
1409573673dSchristos     if (me->outfile != -1)
1419573673dSchristos         do {
1429573673dSchristos             ret = PIECE;
1439573673dSchristos             if ((unsigned)ret > len)
1449573673dSchristos                 ret = (int)len;
1459573673dSchristos             ret = (int)write(me->outfile, buf, ret);
1469573673dSchristos             if (ret == -1)
1479573673dSchristos                 return 1;
1489573673dSchristos             buf += ret;
1499573673dSchristos             len -= ret;
1509573673dSchristos         } while (len != 0);
1519573673dSchristos     return 0;
1529573673dSchristos }
1539573673dSchristos 
1549573673dSchristos /* next input byte macro for use inside lunpipe() and gunpipe() */
1559573673dSchristos #define NEXT() (have ? 0 : (have = in(indp, &next)), \
1569573673dSchristos                 last = have ? (have--, (int)(*next++)) : -1)
1579573673dSchristos 
1589573673dSchristos /* memory for gunpipe() and lunpipe() --
1599573673dSchristos    the first 256 entries of prefix[] and suffix[] are never used, could
1609573673dSchristos    have offset the index, but it's faster to waste the memory */
1619573673dSchristos unsigned char inbuf[SIZE];              /* input buffer */
1629573673dSchristos unsigned char outbuf[SIZE];             /* output buffer */
1639573673dSchristos unsigned short prefix[65536];           /* index to LZW prefix string */
1649573673dSchristos unsigned char suffix[65536];            /* one-character LZW suffix */
1659573673dSchristos unsigned char match[65280 + 2];         /* buffer for reversed match or gzip
1669573673dSchristos                                            32K sliding window */
1679573673dSchristos 
1689573673dSchristos /* throw out what's left in the current bits byte buffer (this is a vestigial
1699573673dSchristos    aspect of the compressed data format derived from an implementation that
1709573673dSchristos    made use of a special VAX machine instruction!) */
1719573673dSchristos #define FLUSHCODE() \
1729573673dSchristos     do { \
1739573673dSchristos         left = 0; \
1749573673dSchristos         rem = 0; \
1759573673dSchristos         if (chunk > have) { \
1769573673dSchristos             chunk -= have; \
1779573673dSchristos             have = 0; \
1789573673dSchristos             if (NEXT() == -1) \
1799573673dSchristos                 break; \
1809573673dSchristos             chunk--; \
1819573673dSchristos             if (chunk > have) { \
1829573673dSchristos                 chunk = have = 0; \
1839573673dSchristos                 break; \
1849573673dSchristos             } \
1859573673dSchristos         } \
1869573673dSchristos         have -= chunk; \
1879573673dSchristos         next += chunk; \
1889573673dSchristos         chunk = 0; \
1899573673dSchristos     } while (0)
1909573673dSchristos 
1919573673dSchristos /* Decompress a compress (LZW) file from indp to outfile.  The compress magic
1929573673dSchristos    header (two bytes) has already been read and verified.  There are have bytes
1939573673dSchristos    of buffered input at next.  strm is used for passing error information back
1949573673dSchristos    to gunpipe().
1959573673dSchristos 
1969573673dSchristos    lunpipe() will return Z_OK on success, Z_BUF_ERROR for an unexpected end of
1979573673dSchristos    file, read error, or write error (a write error indicated by strm->next_in
1989573673dSchristos    not equal to Z_NULL), or Z_DATA_ERROR for invalid input.
1999573673dSchristos  */
lunpipe(unsigned have,z_const unsigned char * next,struct ind * indp,int outfile,z_stream * strm)2008cbf5cb7Schristos local int lunpipe(unsigned have, z_const unsigned char *next, struct ind *indp,
2019573673dSchristos                   int outfile, z_stream *strm)
2029573673dSchristos {
2039573673dSchristos     int last;                   /* last byte read by NEXT(), or -1 if EOF */
2049573673dSchristos     unsigned chunk;             /* bytes left in current chunk */
2059573673dSchristos     int left;                   /* bits left in rem */
2069573673dSchristos     unsigned rem;               /* unused bits from input */
2079573673dSchristos     int bits;                   /* current bits per code */
2089573673dSchristos     unsigned code;              /* code, table traversal index */
2099573673dSchristos     unsigned mask;              /* mask for current bits codes */
2109573673dSchristos     int max;                    /* maximum bits per code for this stream */
2119573673dSchristos     unsigned flags;             /* compress flags, then block compress flag */
2129573673dSchristos     unsigned end;               /* last valid entry in prefix/suffix tables */
2139573673dSchristos     unsigned temp;              /* current code */
2149573673dSchristos     unsigned prev;              /* previous code */
2159573673dSchristos     unsigned final;             /* last character written for previous code */
2169573673dSchristos     unsigned stack;             /* next position for reversed string */
2179573673dSchristos     unsigned outcnt;            /* bytes in output buffer */
2189573673dSchristos     struct outd outd;           /* output structure */
2199573673dSchristos     unsigned char *p;
2209573673dSchristos 
2219573673dSchristos     /* set up output */
2229573673dSchristos     outd.outfile = outfile;
2239573673dSchristos     outd.check = 0;
2249573673dSchristos 
2259573673dSchristos     /* process remainder of compress header -- a flags byte */
2269573673dSchristos     flags = NEXT();
2279573673dSchristos     if (last == -1)
2289573673dSchristos         return Z_BUF_ERROR;
2299573673dSchristos     if (flags & 0x60) {
2309573673dSchristos         strm->msg = (char *)"unknown lzw flags set";
2319573673dSchristos         return Z_DATA_ERROR;
2329573673dSchristos     }
2339573673dSchristos     max = flags & 0x1f;
2349573673dSchristos     if (max < 9 || max > 16) {
2359573673dSchristos         strm->msg = (char *)"lzw bits out of range";
2369573673dSchristos         return Z_DATA_ERROR;
2379573673dSchristos     }
2389573673dSchristos     if (max == 9)                           /* 9 doesn't really mean 9 */
2399573673dSchristos         max = 10;
2409573673dSchristos     flags &= 0x80;                          /* true if block compress */
2419573673dSchristos 
2429573673dSchristos     /* clear table */
2439573673dSchristos     bits = 9;
2449573673dSchristos     mask = 0x1ff;
2459573673dSchristos     end = flags ? 256 : 255;
2469573673dSchristos 
2479573673dSchristos     /* set up: get first 9-bit code, which is the first decompressed byte, but
2489573673dSchristos        don't create a table entry until the next code */
2499573673dSchristos     if (NEXT() == -1)                       /* no compressed data is ok */
2509573673dSchristos         return Z_OK;
2519573673dSchristos     final = prev = (unsigned)last;          /* low 8 bits of code */
2529573673dSchristos     if (NEXT() == -1)                       /* missing a bit */
2539573673dSchristos         return Z_BUF_ERROR;
2549573673dSchristos     if (last & 1) {                         /* code must be < 256 */
2559573673dSchristos         strm->msg = (char *)"invalid lzw code";
2569573673dSchristos         return Z_DATA_ERROR;
2579573673dSchristos     }
2589573673dSchristos     rem = (unsigned)last >> 1;              /* remaining 7 bits */
2599573673dSchristos     left = 7;
2609573673dSchristos     chunk = bits - 2;                       /* 7 bytes left in this chunk */
2619573673dSchristos     outbuf[0] = (unsigned char)final;       /* write first decompressed byte */
2629573673dSchristos     outcnt = 1;
2639573673dSchristos 
2649573673dSchristos     /* decode codes */
2659573673dSchristos     stack = 0;
2669573673dSchristos     for (;;) {
2679573673dSchristos         /* if the table will be full after this, increment the code size */
2689573673dSchristos         if (end >= mask && bits < max) {
2699573673dSchristos             FLUSHCODE();
2709573673dSchristos             bits++;
2719573673dSchristos             mask <<= 1;
2729573673dSchristos             mask++;
2739573673dSchristos         }
2749573673dSchristos 
2759573673dSchristos         /* get a code of length bits */
2769573673dSchristos         if (chunk == 0)                     /* decrement chunk modulo bits */
2779573673dSchristos             chunk = bits;
2789573673dSchristos         code = rem;                         /* low bits of code */
2799573673dSchristos         if (NEXT() == -1) {                 /* EOF is end of compressed data */
2809573673dSchristos             /* write remaining buffered output */
2819573673dSchristos             if (outcnt && out(&outd, outbuf, outcnt)) {
2829573673dSchristos                 strm->next_in = outbuf;     /* signal write error */
2839573673dSchristos                 return Z_BUF_ERROR;
2849573673dSchristos             }
2859573673dSchristos             return Z_OK;
2869573673dSchristos         }
2879573673dSchristos         code += (unsigned)last << left;     /* middle (or high) bits of code */
2889573673dSchristos         left += 8;
2899573673dSchristos         chunk--;
2909573673dSchristos         if (bits > left) {                  /* need more bits */
2919573673dSchristos             if (NEXT() == -1)               /* can't end in middle of code */
2929573673dSchristos                 return Z_BUF_ERROR;
2939573673dSchristos             code += (unsigned)last << left; /* high bits of code */
2949573673dSchristos             left += 8;
2959573673dSchristos             chunk--;
2969573673dSchristos         }
2979573673dSchristos         code &= mask;                       /* mask to current code length */
2989573673dSchristos         left -= bits;                       /* number of unused bits */
2999573673dSchristos         rem = (unsigned)last >> (8 - left); /* unused bits from last byte */
3009573673dSchristos 
3019573673dSchristos         /* process clear code (256) */
3029573673dSchristos         if (code == 256 && flags) {
3039573673dSchristos             FLUSHCODE();
3049573673dSchristos             bits = 9;                       /* initialize bits and mask */
3059573673dSchristos             mask = 0x1ff;
3069573673dSchristos             end = 255;                      /* empty table */
3079573673dSchristos             continue;                       /* get next code */
3089573673dSchristos         }
3099573673dSchristos 
3109573673dSchristos         /* special code to reuse last match */
3119573673dSchristos         temp = code;                        /* save the current code */
3129573673dSchristos         if (code > end) {
3139573673dSchristos             /* Be picky on the allowed code here, and make sure that the code
3149573673dSchristos                we drop through (prev) will be a valid index so that random
3159573673dSchristos                input does not cause an exception.  The code != end + 1 check is
3169573673dSchristos                empirically derived, and not checked in the original uncompress
3179573673dSchristos                code.  If this ever causes a problem, that check could be safely
3189573673dSchristos                removed.  Leaving this check in greatly improves gun's ability
3199573673dSchristos                to detect random or corrupted input after a compress header.
3209573673dSchristos                In any case, the prev > end check must be retained. */
3219573673dSchristos             if (code != end + 1 || prev > end) {
3229573673dSchristos                 strm->msg = (char *)"invalid lzw code";
3239573673dSchristos                 return Z_DATA_ERROR;
3249573673dSchristos             }
3259573673dSchristos             match[stack++] = (unsigned char)final;
3269573673dSchristos             code = prev;
3279573673dSchristos         }
3289573673dSchristos 
3299573673dSchristos         /* walk through linked list to generate output in reverse order */
3309573673dSchristos         p = match + stack;
3319573673dSchristos         while (code >= 256) {
3329573673dSchristos             *p++ = suffix[code];
3339573673dSchristos             code = prefix[code];
3349573673dSchristos         }
3359573673dSchristos         stack = p - match;
3369573673dSchristos         match[stack++] = (unsigned char)code;
3379573673dSchristos         final = code;
3389573673dSchristos 
3399573673dSchristos         /* link new table entry */
3409573673dSchristos         if (end < mask) {
3419573673dSchristos             end++;
3429573673dSchristos             prefix[end] = (unsigned short)prev;
3439573673dSchristos             suffix[end] = (unsigned char)final;
3449573673dSchristos         }
3459573673dSchristos 
3469573673dSchristos         /* set previous code for next iteration */
3479573673dSchristos         prev = temp;
3489573673dSchristos 
3499573673dSchristos         /* write output in forward order */
3509573673dSchristos         while (stack > SIZE - outcnt) {
3519573673dSchristos             while (outcnt < SIZE)
3529573673dSchristos                 outbuf[outcnt++] = match[--stack];
3539573673dSchristos             if (out(&outd, outbuf, outcnt)) {
3549573673dSchristos                 strm->next_in = outbuf; /* signal write error */
3559573673dSchristos                 return Z_BUF_ERROR;
3569573673dSchristos             }
3579573673dSchristos             outcnt = 0;
3589573673dSchristos         }
3599573673dSchristos         p = match + stack;
3609573673dSchristos         do {
3619573673dSchristos             outbuf[outcnt++] = *--p;
3629573673dSchristos         } while (p > match);
3639573673dSchristos         stack = 0;
3649573673dSchristos 
3659573673dSchristos         /* loop for next code with final and prev as the last match, rem and
3669573673dSchristos            left provide the first 0..7 bits of the next code, end is the last
3679573673dSchristos            valid table entry */
3689573673dSchristos     }
3699573673dSchristos }
3709573673dSchristos 
3719573673dSchristos /* Decompress a gzip file from infile to outfile.  strm is assumed to have been
3729573673dSchristos    successfully initialized with inflateBackInit().  The input file may consist
3739573673dSchristos    of a series of gzip streams, in which case all of them will be decompressed
3749573673dSchristos    to the output file.  If outfile is -1, then the gzip stream(s) integrity is
3759573673dSchristos    checked and nothing is written.
3769573673dSchristos 
3779573673dSchristos    The return value is a zlib error code: Z_MEM_ERROR if out of memory,
3789573673dSchristos    Z_DATA_ERROR if the header or the compressed data is invalid, or if the
3799573673dSchristos    trailer CRC-32 check or length doesn't match, Z_BUF_ERROR if the input ends
3809573673dSchristos    prematurely or a write error occurs, or Z_ERRNO if junk (not a another gzip
3819573673dSchristos    stream) follows a valid gzip stream.
3829573673dSchristos  */
gunpipe(z_stream * strm,int infile,int outfile)3839573673dSchristos local int gunpipe(z_stream *strm, int infile, int outfile)
3849573673dSchristos {
3859573673dSchristos     int ret, first, last;
3869573673dSchristos     unsigned have, flags, len;
3878cbf5cb7Schristos     z_const unsigned char *next = NULL;
3889573673dSchristos     struct ind ind, *indp;
3899573673dSchristos     struct outd outd;
3909573673dSchristos 
3919573673dSchristos     /* setup input buffer */
3929573673dSchristos     ind.infile = infile;
3939573673dSchristos     ind.inbuf = inbuf;
3949573673dSchristos     indp = &ind;
3959573673dSchristos 
3969573673dSchristos     /* decompress concatenated gzip streams */
3979573673dSchristos     have = 0;                               /* no input data read in yet */
3989573673dSchristos     first = 1;                              /* looking for first gzip header */
3999573673dSchristos     strm->next_in = Z_NULL;                 /* so Z_BUF_ERROR means EOF */
4009573673dSchristos     for (;;) {
4019573673dSchristos         /* look for the two magic header bytes for a gzip stream */
4029573673dSchristos         if (NEXT() == -1) {
4039573673dSchristos             ret = Z_OK;
4049573673dSchristos             break;                          /* empty gzip stream is ok */
4059573673dSchristos         }
4069573673dSchristos         if (last != 31 || (NEXT() != 139 && last != 157)) {
4079573673dSchristos             strm->msg = (char *)"incorrect header check";
4089573673dSchristos             ret = first ? Z_DATA_ERROR : Z_ERRNO;
4099573673dSchristos             break;                          /* not a gzip or compress header */
4109573673dSchristos         }
4119573673dSchristos         first = 0;                          /* next non-header is junk */
4129573673dSchristos 
4139573673dSchristos         /* process a compress (LZW) file -- can't be concatenated after this */
4149573673dSchristos         if (last == 157) {
4159573673dSchristos             ret = lunpipe(have, next, indp, outfile, strm);
4169573673dSchristos             break;
4179573673dSchristos         }
4189573673dSchristos 
4199573673dSchristos         /* process remainder of gzip header */
4209573673dSchristos         ret = Z_BUF_ERROR;
4219573673dSchristos         if (NEXT() != 8) {                  /* only deflate method allowed */
4229573673dSchristos             if (last == -1) break;
4239573673dSchristos             strm->msg = (char *)"unknown compression method";
4249573673dSchristos             ret = Z_DATA_ERROR;
4259573673dSchristos             break;
4269573673dSchristos         }
4279573673dSchristos         flags = NEXT();                     /* header flags */
4289573673dSchristos         NEXT();                             /* discard mod time, xflgs, os */
4299573673dSchristos         NEXT();
4309573673dSchristos         NEXT();
4319573673dSchristos         NEXT();
4329573673dSchristos         NEXT();
4339573673dSchristos         NEXT();
4349573673dSchristos         if (last == -1) break;
4359573673dSchristos         if (flags & 0xe0) {
4369573673dSchristos             strm->msg = (char *)"unknown header flags set";
4379573673dSchristos             ret = Z_DATA_ERROR;
4389573673dSchristos             break;
4399573673dSchristos         }
4409573673dSchristos         if (flags & 4) {                    /* extra field */
4419573673dSchristos             len = NEXT();
4429573673dSchristos             len += (unsigned)(NEXT()) << 8;
4439573673dSchristos             if (last == -1) break;
4449573673dSchristos             while (len > have) {
4459573673dSchristos                 len -= have;
4469573673dSchristos                 have = 0;
4479573673dSchristos                 if (NEXT() == -1) break;
4489573673dSchristos                 len--;
4499573673dSchristos             }
4509573673dSchristos             if (last == -1) break;
4519573673dSchristos             have -= len;
4529573673dSchristos             next += len;
4539573673dSchristos         }
4549573673dSchristos         if (flags & 8)                      /* file name */
4559573673dSchristos             while (NEXT() != 0 && last != -1)
4569573673dSchristos                 ;
4579573673dSchristos         if (flags & 16)                     /* comment */
4589573673dSchristos             while (NEXT() != 0 && last != -1)
4599573673dSchristos                 ;
4609573673dSchristos         if (flags & 2) {                    /* header crc */
4619573673dSchristos             NEXT();
4629573673dSchristos             NEXT();
4639573673dSchristos         }
4649573673dSchristos         if (last == -1) break;
4659573673dSchristos 
4669573673dSchristos         /* set up output */
4679573673dSchristos         outd.outfile = outfile;
4689573673dSchristos         outd.check = 1;
4699573673dSchristos         outd.crc = crc32(0L, Z_NULL, 0);
4709573673dSchristos         outd.total = 0;
4719573673dSchristos 
4729573673dSchristos         /* decompress data to output */
4739573673dSchristos         strm->next_in = next;
4749573673dSchristos         strm->avail_in = have;
4759573673dSchristos         ret = inflateBack(strm, in, indp, out, &outd);
4769573673dSchristos         if (ret != Z_STREAM_END) break;
4779573673dSchristos         next = strm->next_in;
4789573673dSchristos         have = strm->avail_in;
4799573673dSchristos         strm->next_in = Z_NULL;             /* so Z_BUF_ERROR means EOF */
4809573673dSchristos 
4819573673dSchristos         /* check trailer */
4829573673dSchristos         ret = Z_BUF_ERROR;
4839573673dSchristos         if (NEXT() != (int)(outd.crc & 0xff) ||
4849573673dSchristos             NEXT() != (int)((outd.crc >> 8) & 0xff) ||
4859573673dSchristos             NEXT() != (int)((outd.crc >> 16) & 0xff) ||
4869573673dSchristos             NEXT() != (int)((outd.crc >> 24) & 0xff)) {
4879573673dSchristos             /* crc error */
4889573673dSchristos             if (last != -1) {
4899573673dSchristos                 strm->msg = (char *)"incorrect data check";
4909573673dSchristos                 ret = Z_DATA_ERROR;
4919573673dSchristos             }
4929573673dSchristos             break;
4939573673dSchristos         }
4949573673dSchristos         if (NEXT() != (int)(outd.total & 0xff) ||
4959573673dSchristos             NEXT() != (int)((outd.total >> 8) & 0xff) ||
4969573673dSchristos             NEXT() != (int)((outd.total >> 16) & 0xff) ||
4979573673dSchristos             NEXT() != (int)((outd.total >> 24) & 0xff)) {
4989573673dSchristos             /* length error */
4999573673dSchristos             if (last != -1) {
5009573673dSchristos                 strm->msg = (char *)"incorrect length check";
5019573673dSchristos                 ret = Z_DATA_ERROR;
5029573673dSchristos             }
5039573673dSchristos             break;
5049573673dSchristos         }
5059573673dSchristos 
5069573673dSchristos         /* go back and look for another gzip stream */
5079573673dSchristos     }
5089573673dSchristos 
5099573673dSchristos     /* clean up and return */
5109573673dSchristos     return ret;
5119573673dSchristos }
5129573673dSchristos 
5139573673dSchristos /* Copy file attributes, from -> to, as best we can.  This is best effort, so
5149573673dSchristos    no errors are reported.  The mode bits, including suid, sgid, and the sticky
5159573673dSchristos    bit are copied (if allowed), the owner's user id and group id are copied
5169573673dSchristos    (again if allowed), and the access and modify times are copied. */
copymeta(char * from,char * to)5179573673dSchristos local void copymeta(char *from, char *to)
5189573673dSchristos {
5199573673dSchristos     struct stat was;
5209573673dSchristos     struct utimbuf when;
5219573673dSchristos 
5229573673dSchristos     /* get all of from's Unix meta data, return if not a regular file */
5239573673dSchristos     if (stat(from, &was) != 0 || (was.st_mode & S_IFMT) != S_IFREG)
5249573673dSchristos         return;
5259573673dSchristos 
5269573673dSchristos     /* set to's mode bits, ignore errors */
5279573673dSchristos     (void)chmod(to, was.st_mode & 07777);
5289573673dSchristos 
5299573673dSchristos     /* copy owner's user and group, ignore errors */
5309573673dSchristos     (void)chown(to, was.st_uid, was.st_gid);
5319573673dSchristos 
5329573673dSchristos     /* copy access and modify times, ignore errors */
5339573673dSchristos     when.actime = was.st_atime;
5349573673dSchristos     when.modtime = was.st_mtime;
5359573673dSchristos     (void)utime(to, &when);
5369573673dSchristos }
5379573673dSchristos 
5389573673dSchristos /* Decompress the file inname to the file outnname, of if test is true, just
5399573673dSchristos    decompress without writing and check the gzip trailer for integrity.  If
5409573673dSchristos    inname is NULL or an empty string, read from stdin.  If outname is NULL or
5419573673dSchristos    an empty string, write to stdout.  strm is a pre-initialized inflateBack
5429573673dSchristos    structure.  When appropriate, copy the file attributes from inname to
5439573673dSchristos    outname.
5449573673dSchristos 
5459573673dSchristos    gunzip() returns 1 if there is an out-of-memory error or an unexpected
5469573673dSchristos    return code from gunpipe().  Otherwise it returns 0.
5479573673dSchristos  */
gunzip(z_stream * strm,char * inname,char * outname,int test)5489573673dSchristos local int gunzip(z_stream *strm, char *inname, char *outname, int test)
5499573673dSchristos {
5509573673dSchristos     int ret;
5519573673dSchristos     int infile, outfile;
5529573673dSchristos 
5539573673dSchristos     /* open files */
5549573673dSchristos     if (inname == NULL || *inname == 0) {
5559573673dSchristos         inname = "-";
5569573673dSchristos         infile = 0;     /* stdin */
5579573673dSchristos     }
5589573673dSchristos     else {
5599573673dSchristos         infile = open(inname, O_RDONLY, 0);
5609573673dSchristos         if (infile == -1) {
5619573673dSchristos             fprintf(stderr, "gun cannot open %s\n", inname);
5629573673dSchristos             return 0;
5639573673dSchristos         }
5649573673dSchristos     }
5659573673dSchristos     if (test)
5669573673dSchristos         outfile = -1;
5679573673dSchristos     else if (outname == NULL || *outname == 0) {
5689573673dSchristos         outname = "-";
5699573673dSchristos         outfile = 1;    /* stdout */
5709573673dSchristos     }
5719573673dSchristos     else {
5729573673dSchristos         outfile = open(outname, O_CREAT | O_TRUNC | O_WRONLY, 0666);
5739573673dSchristos         if (outfile == -1) {
5749573673dSchristos             close(infile);
5759573673dSchristos             fprintf(stderr, "gun cannot create %s\n", outname);
5769573673dSchristos             return 0;
5779573673dSchristos         }
5789573673dSchristos     }
5799573673dSchristos     errno = 0;
5809573673dSchristos 
5819573673dSchristos     /* decompress */
5829573673dSchristos     ret = gunpipe(strm, infile, outfile);
5839573673dSchristos     if (outfile > 2) close(outfile);
5849573673dSchristos     if (infile > 2) close(infile);
5859573673dSchristos 
5869573673dSchristos     /* interpret result */
5879573673dSchristos     switch (ret) {
5889573673dSchristos     case Z_OK:
5899573673dSchristos     case Z_ERRNO:
5909573673dSchristos         if (infile > 2 && outfile > 2) {
5919573673dSchristos             copymeta(inname, outname);          /* copy attributes */
5929573673dSchristos             unlink(inname);
5939573673dSchristos         }
5949573673dSchristos         if (ret == Z_ERRNO)
5959573673dSchristos             fprintf(stderr, "gun warning: trailing garbage ignored in %s\n",
5969573673dSchristos                     inname);
5979573673dSchristos         break;
5989573673dSchristos     case Z_DATA_ERROR:
5999573673dSchristos         if (outfile > 2) unlink(outname);
6009573673dSchristos         fprintf(stderr, "gun data error on %s: %s\n", inname, strm->msg);
6019573673dSchristos         break;
6029573673dSchristos     case Z_MEM_ERROR:
6039573673dSchristos         if (outfile > 2) unlink(outname);
6049573673dSchristos         fprintf(stderr, "gun out of memory error--aborting\n");
6059573673dSchristos         return 1;
6069573673dSchristos     case Z_BUF_ERROR:
6079573673dSchristos         if (outfile > 2) unlink(outname);
6089573673dSchristos         if (strm->next_in != Z_NULL) {
6099573673dSchristos             fprintf(stderr, "gun write error on %s: %s\n",
6109573673dSchristos                     outname, strerror(errno));
6119573673dSchristos         }
6129573673dSchristos         else if (errno) {
6139573673dSchristos             fprintf(stderr, "gun read error on %s: %s\n",
6149573673dSchristos                     inname, strerror(errno));
6159573673dSchristos         }
6169573673dSchristos         else {
6179573673dSchristos             fprintf(stderr, "gun unexpected end of file on %s\n",
6189573673dSchristos                     inname);
6199573673dSchristos         }
6209573673dSchristos         break;
6219573673dSchristos     default:
6229573673dSchristos         if (outfile > 2) unlink(outname);
6239573673dSchristos         fprintf(stderr, "gun internal error--aborting\n");
6249573673dSchristos         return 1;
6259573673dSchristos     }
6269573673dSchristos     return 0;
6279573673dSchristos }
6289573673dSchristos 
6299573673dSchristos /* Process the gun command line arguments.  See the command syntax near the
6309573673dSchristos    beginning of this source file. */
main(int argc,char ** argv)6319573673dSchristos int main(int argc, char **argv)
6329573673dSchristos {
6339573673dSchristos     int ret, len, test;
6349573673dSchristos     char *outname;
6359573673dSchristos     unsigned char *window;
6369573673dSchristos     z_stream strm;
6379573673dSchristos 
6389573673dSchristos     /* initialize inflateBack state for repeated use */
6399573673dSchristos     window = match;                         /* reuse LZW match buffer */
6409573673dSchristos     strm.zalloc = Z_NULL;
6419573673dSchristos     strm.zfree = Z_NULL;
6429573673dSchristos     strm.opaque = Z_NULL;
6439573673dSchristos     ret = inflateBackInit(&strm, 15, window);
6449573673dSchristos     if (ret != Z_OK) {
6459573673dSchristos         fprintf(stderr, "gun out of memory error--aborting\n");
6469573673dSchristos         return 1;
6479573673dSchristos     }
6489573673dSchristos 
6499573673dSchristos     /* decompress each file to the same name with the suffix removed */
6509573673dSchristos     argc--;
6519573673dSchristos     argv++;
6529573673dSchristos     test = 0;
6539573673dSchristos     if (argc && strcmp(*argv, "-h") == 0) {
6549573673dSchristos         fprintf(stderr, "gun 1.6 (17 Jan 2010)\n");
6559573673dSchristos         fprintf(stderr, "Copyright (C) 2003-2010 Mark Adler\n");
6569573673dSchristos         fprintf(stderr, "usage: gun [-t] [file1.gz [file2.Z ...]]\n");
6579573673dSchristos         return 0;
6589573673dSchristos     }
6599573673dSchristos     if (argc && strcmp(*argv, "-t") == 0) {
6609573673dSchristos         test = 1;
6619573673dSchristos         argc--;
6629573673dSchristos         argv++;
6639573673dSchristos     }
6649573673dSchristos     if (argc)
6659573673dSchristos         do {
6669573673dSchristos             if (test)
6679573673dSchristos                 outname = NULL;
6689573673dSchristos             else {
6699573673dSchristos                 len = (int)strlen(*argv);
6709573673dSchristos                 if (strcmp(*argv + len - 3, ".gz") == 0 ||
6719573673dSchristos                     strcmp(*argv + len - 3, "-gz") == 0)
6729573673dSchristos                     len -= 3;
6739573673dSchristos                 else if (strcmp(*argv + len - 2, ".z") == 0 ||
6749573673dSchristos                     strcmp(*argv + len - 2, "-z") == 0 ||
6759573673dSchristos                     strcmp(*argv + len - 2, "_z") == 0 ||
6769573673dSchristos                     strcmp(*argv + len - 2, ".Z") == 0)
6779573673dSchristos                     len -= 2;
6789573673dSchristos                 else {
6799573673dSchristos                     fprintf(stderr, "gun error: no gz type on %s--skipping\n",
6809573673dSchristos                             *argv);
6819573673dSchristos                     continue;
6829573673dSchristos                 }
6839573673dSchristos                 outname = malloc(len + 1);
6849573673dSchristos                 if (outname == NULL) {
6859573673dSchristos                     fprintf(stderr, "gun out of memory error--aborting\n");
6869573673dSchristos                     ret = 1;
6879573673dSchristos                     break;
6889573673dSchristos                 }
6899573673dSchristos                 memcpy(outname, *argv, len);
6909573673dSchristos                 outname[len] = 0;
6919573673dSchristos             }
6929573673dSchristos             ret = gunzip(&strm, *argv, outname, test);
6939573673dSchristos             if (outname != NULL) free(outname);
6949573673dSchristos             if (ret) break;
6959573673dSchristos         } while (argv++, --argc);
6969573673dSchristos     else
6979573673dSchristos         ret = gunzip(&strm, NULL, NULL, test);
6989573673dSchristos 
6999573673dSchristos     /* clean up */
7009573673dSchristos     inflateBackEnd(&strm);
7019573673dSchristos     return ret;
7029573673dSchristos }
703