kusano fc6ab3
/* gun.c -- simple gunzip to give an example of the use of inflateBack()
kusano fc6ab3
 * Copyright (C) 2003, 2005, 2008, 2010, 2012 Mark Adler
kusano fc6ab3
 * For conditions of distribution and use, see copyright notice in zlib.h
kusano fc6ab3
   Version 1.7  12 August 2012  Mark Adler */
kusano fc6ab3
kusano fc6ab3
/* Version history:
kusano fc6ab3
   1.0  16 Feb 2003  First version for testing of inflateBack()
kusano fc6ab3
   1.1  21 Feb 2005  Decompress concatenated gzip streams
kusano fc6ab3
                     Remove use of "this" variable (C++ keyword)
kusano fc6ab3
                     Fix return value for in()
kusano fc6ab3
                     Improve allocation failure checking
kusano fc6ab3
                     Add typecasting for void * structures
kusano fc6ab3
                     Add -h option for command version and usage
kusano fc6ab3
                     Add a bunch of comments
kusano fc6ab3
   1.2  20 Mar 2005  Add Unix compress (LZW) decompression
kusano fc6ab3
                     Copy file attributes from input file to output file
kusano fc6ab3
   1.3  12 Jun 2005  Add casts for error messages [Oberhumer]
kusano fc6ab3
   1.4   8 Dec 2006  LZW decompression speed improvements
kusano fc6ab3
   1.5   9 Feb 2008  Avoid warning in latest version of gcc
kusano fc6ab3
   1.6  17 Jan 2010  Avoid signed/unsigned comparison warnings
kusano fc6ab3
   1.7  12 Aug 2012  Update for z_const usage in zlib 1.2.8
kusano fc6ab3
 */
kusano fc6ab3
kusano fc6ab3
/*
kusano fc6ab3
   gun [ -t ] [ name ... ]
kusano fc6ab3
kusano fc6ab3
   decompresses the data in the named gzip files.  If no arguments are given,
kusano fc6ab3
   gun will decompress from stdin to stdout.  The names must end in .gz, -gz,
kusano fc6ab3
   .z, -z, _z, or .Z.  The uncompressed data will be written to a file name
kusano fc6ab3
   with the suffix stripped.  On success, the original file is deleted.  On
kusano fc6ab3
   failure, the output file is deleted.  For most failures, the command will
kusano fc6ab3
   continue to process the remaining names on the command line.  A memory
kusano fc6ab3
   allocation failure will abort the command.  If -t is specified, then the
kusano fc6ab3
   listed files or stdin will be tested as gzip files for integrity (without
kusano fc6ab3
   checking for a proper suffix), no output will be written, and no files
kusano fc6ab3
   will be deleted.
kusano fc6ab3
kusano fc6ab3
   Like gzip, gun allows concatenated gzip streams and will decompress them,
kusano fc6ab3
   writing all of the uncompressed data to the output.  Unlike gzip, gun allows
kusano fc6ab3
   an empty file on input, and will produce no error writing an empty output
kusano fc6ab3
   file.
kusano fc6ab3
kusano fc6ab3
   gun will also decompress files made by Unix compress, which uses LZW
kusano fc6ab3
   compression.  These files are automatically detected by virtue of their
kusano fc6ab3
   magic header bytes.  Since the end of Unix compress stream is marked by the
kusano fc6ab3
   end-of-file, they cannot be concantenated.  If a Unix compress stream is
kusano fc6ab3
   encountered in an input file, it is the last stream in that file.
kusano fc6ab3
kusano fc6ab3
   Like gunzip and uncompress, the file attributes of the orignal compressed
kusano fc6ab3
   file are maintained in the final uncompressed file, to the extent that the
kusano fc6ab3
   user permissions allow it.
kusano fc6ab3
kusano fc6ab3
   On my Mac OS X PowerPC G4, gun is almost twice as fast as gunzip (version
kusano fc6ab3
   1.2.4) is on the same file, when gun is linked with zlib 1.2.2.  Also the
kusano fc6ab3
   LZW decompression provided by gun is about twice as fast as the standard
kusano fc6ab3
   Unix uncompress command.
kusano fc6ab3
 */
kusano fc6ab3
kusano fc6ab3
/* external functions and related types and constants */
kusano fc6ab3
#include <stdio.h>          /* fprintf() */</stdio.h>
kusano fc6ab3
#include <stdlib.h>         /* malloc(), free() */</stdlib.h>
kusano fc6ab3
#include <string.h>         /* strerror(), strcmp(), strlen(), memcpy() */</string.h>
kusano fc6ab3
#include <errno.h>          /* errno */</errno.h>
kusano fc6ab3
#include <fcntl.h>          /* open() */</fcntl.h>
kusano fc6ab3
#include <unistd.h>         /* read(), write(), close(), chown(), unlink() */</unistd.h>
kusano fc6ab3
#include <sys types.h=""></sys>
kusano fc6ab3
#include <sys stat.h="">       /* stat(), chmod() */</sys>
kusano fc6ab3
#include <utime.h>          /* utime() */</utime.h>
kusano fc6ab3
#include "zlib.h"           /* inflateBackInit(), inflateBack(), */
kusano fc6ab3
                            /* inflateBackEnd(), crc32() */
kusano fc6ab3
kusano fc6ab3
/* function declaration */
kusano fc6ab3
#define local static
kusano fc6ab3
kusano fc6ab3
/* buffer constants */
kusano fc6ab3
#define SIZE 32768U         /* input and output buffer sizes */
kusano fc6ab3
#define PIECE 16384         /* limits i/o chunks for 16-bit int case */
kusano fc6ab3
kusano fc6ab3
/* structure for infback() to pass to input function in() -- it maintains the
kusano fc6ab3
   input file and a buffer of size SIZE */
kusano fc6ab3
struct ind {
kusano fc6ab3
    int infile;
kusano fc6ab3
    unsigned char *inbuf;
kusano fc6ab3
};
kusano fc6ab3
kusano fc6ab3
/* Load input buffer, assumed to be empty, and return bytes loaded and a
kusano fc6ab3
   pointer to them.  read() is called until the buffer is full, or until it
kusano fc6ab3
   returns end-of-file or error.  Return 0 on error. */
kusano fc6ab3
local unsigned in(void *in_desc, z_const unsigned char **buf)
kusano fc6ab3
{
kusano fc6ab3
    int ret;
kusano fc6ab3
    unsigned len;
kusano fc6ab3
    unsigned char *next;
kusano fc6ab3
    struct ind *me = (struct ind *)in_desc;
kusano fc6ab3
kusano fc6ab3
    next = me->inbuf;
kusano fc6ab3
    *buf = next;
kusano fc6ab3
    len = 0;
kusano fc6ab3
    do {
kusano fc6ab3
        ret = PIECE;
kusano fc6ab3
        if ((unsigned)ret > SIZE - len)
kusano fc6ab3
            ret = (int)(SIZE - len);
kusano fc6ab3
        ret = (int)read(me->infile, next, ret);
kusano fc6ab3
        if (ret == -1) {
kusano fc6ab3
            len = 0;
kusano fc6ab3
            break;
kusano fc6ab3
        }
kusano fc6ab3
        next += ret;
kusano fc6ab3
        len += ret;
kusano fc6ab3
    } while (ret != 0 && len < SIZE);
kusano fc6ab3
    return len;
kusano fc6ab3
}
kusano fc6ab3
kusano fc6ab3
/* structure for infback() to pass to output function out() -- it maintains the
kusano fc6ab3
   output file, a running CRC-32 check on the output and the total number of
kusano fc6ab3
   bytes output, both for checking against the gzip trailer.  (The length in
kusano fc6ab3
   the gzip trailer is stored modulo 2^32, so it's ok if a long is 32 bits and
kusano fc6ab3
   the output is greater than 4 GB.) */
kusano fc6ab3
struct outd {
kusano fc6ab3
    int outfile;
kusano fc6ab3
    int check;                  /* true if checking crc and total */
kusano fc6ab3
    unsigned long crc;
kusano fc6ab3
    unsigned long total;
kusano fc6ab3
};
kusano fc6ab3
kusano fc6ab3
/* Write output buffer and update the CRC-32 and total bytes written.  write()
kusano fc6ab3
   is called until all of the output is written or an error is encountered.
kusano fc6ab3
   On success out() returns 0.  For a write failure, out() returns 1.  If the
kusano fc6ab3
   output file descriptor is -1, then nothing is written.
kusano fc6ab3
 */
kusano fc6ab3
local int out(void *out_desc, unsigned char *buf, unsigned len)
kusano fc6ab3
{
kusano fc6ab3
    int ret;
kusano fc6ab3
    struct outd *me = (struct outd *)out_desc;
kusano fc6ab3
kusano fc6ab3
    if (me->check) {
kusano fc6ab3
        me->crc = crc32(me->crc, buf, len);
kusano fc6ab3
        me->total += len;
kusano fc6ab3
    }
kusano fc6ab3
    if (me->outfile != -1)
kusano fc6ab3
        do {
kusano fc6ab3
            ret = PIECE;
kusano fc6ab3
            if ((unsigned)ret > len)
kusano fc6ab3
                ret = (int)len;
kusano fc6ab3
            ret = (int)write(me->outfile, buf, ret);
kusano fc6ab3
            if (ret == -1)
kusano fc6ab3
                return 1;
kusano fc6ab3
            buf += ret;
kusano fc6ab3
            len -= ret;
kusano fc6ab3
        } while (len != 0);
kusano fc6ab3
    return 0;
kusano fc6ab3
}
kusano fc6ab3
kusano fc6ab3
/* next input byte macro for use inside lunpipe() and gunpipe() */
kusano fc6ab3
#define NEXT() (have ? 0 : (have = in(indp, &next)), \
kusano fc6ab3
                last = have ? (have--, (int)(*next++)) : -1)
kusano fc6ab3
kusano fc6ab3
/* memory for gunpipe() and lunpipe() --
kusano fc6ab3
   the first 256 entries of prefix[] and suffix[] are never used, could
kusano fc6ab3
   have offset the index, but it's faster to waste the memory */
kusano fc6ab3
unsigned char inbuf[SIZE];              /* input buffer */
kusano fc6ab3
unsigned char outbuf[SIZE];             /* output buffer */
kusano fc6ab3
unsigned short prefix[65536];           /* index to LZW prefix string */
kusano fc6ab3
unsigned char suffix[65536];            /* one-character LZW suffix */
kusano fc6ab3
unsigned char match[65280 + 2];         /* buffer for reversed match or gzip
kusano fc6ab3
                                           32K sliding window */
kusano fc6ab3
kusano fc6ab3
/* throw out what's left in the current bits byte buffer (this is a vestigial
kusano fc6ab3
   aspect of the compressed data format derived from an implementation that
kusano fc6ab3
   made use of a special VAX machine instruction!) */
kusano fc6ab3
#define FLUSHCODE() \
kusano fc6ab3
    do { \
kusano fc6ab3
        left = 0; \
kusano fc6ab3
        rem = 0; \
kusano fc6ab3
        if (chunk > have) { \
kusano fc6ab3
            chunk -= have; \
kusano fc6ab3
            have = 0; \
kusano fc6ab3
            if (NEXT() == -1) \
kusano fc6ab3
                break; \
kusano fc6ab3
            chunk--; \
kusano fc6ab3
            if (chunk > have) { \
kusano fc6ab3
                chunk = have = 0; \
kusano fc6ab3
                break; \
kusano fc6ab3
            } \
kusano fc6ab3
        } \
kusano fc6ab3
        have -= chunk; \
kusano fc6ab3
        next += chunk; \
kusano fc6ab3
        chunk = 0; \
kusano fc6ab3
    } while (0)
kusano fc6ab3
kusano fc6ab3
/* Decompress a compress (LZW) file from indp to outfile.  The compress magic
kusano fc6ab3
   header (two bytes) has already been read and verified.  There are have bytes
kusano fc6ab3
   of buffered input at next.  strm is used for passing error information back
kusano fc6ab3
   to gunpipe().
kusano fc6ab3
kusano fc6ab3
   lunpipe() will return Z_OK on success, Z_BUF_ERROR for an unexpected end of
kusano fc6ab3
   file, read error, or write error (a write error indicated by strm->next_in
kusano fc6ab3
   not equal to Z_NULL), or Z_DATA_ERROR for invalid input.
kusano fc6ab3
 */
kusano fc6ab3
local int lunpipe(unsigned have, z_const unsigned char *next, struct ind *indp,
kusano fc6ab3
                  int outfile, z_stream *strm)
kusano fc6ab3
{
kusano fc6ab3
    int last;                   /* last byte read by NEXT(), or -1 if EOF */
kusano fc6ab3
    unsigned chunk;             /* bytes left in current chunk */
kusano fc6ab3
    int left;                   /* bits left in rem */
kusano fc6ab3
    unsigned rem;               /* unused bits from input */
kusano fc6ab3
    int bits;                   /* current bits per code */
kusano fc6ab3
    unsigned code;              /* code, table traversal index */
kusano fc6ab3
    unsigned mask;              /* mask for current bits codes */
kusano fc6ab3
    int max;                    /* maximum bits per code for this stream */
kusano fc6ab3
    unsigned flags;             /* compress flags, then block compress flag */
kusano fc6ab3
    unsigned end;               /* last valid entry in prefix/suffix tables */
kusano fc6ab3
    unsigned temp;              /* current code */
kusano fc6ab3
    unsigned prev;              /* previous code */
kusano fc6ab3
    unsigned final;             /* last character written for previous code */
kusano fc6ab3
    unsigned stack;             /* next position for reversed string */
kusano fc6ab3
    unsigned outcnt;            /* bytes in output buffer */
kusano fc6ab3
    struct outd outd;           /* output structure */
kusano fc6ab3
    unsigned char *p;
kusano fc6ab3
kusano fc6ab3
    /* set up output */
kusano fc6ab3
    outd.outfile = outfile;
kusano fc6ab3
    outd.check = 0;
kusano fc6ab3
kusano fc6ab3
    /* process remainder of compress header -- a flags byte */
kusano fc6ab3
    flags = NEXT();
kusano fc6ab3
    if (last == -1)
kusano fc6ab3
        return Z_BUF_ERROR;
kusano fc6ab3
    if (flags & 0x60) {
kusano fc6ab3
        strm->msg = (char *)"unknown lzw flags set";
kusano fc6ab3
        return Z_DATA_ERROR;
kusano fc6ab3
    }
kusano fc6ab3
    max = flags & 0x1f;
kusano fc6ab3
    if (max < 9 || max > 16) {
kusano fc6ab3
        strm->msg = (char *)"lzw bits out of range";
kusano fc6ab3
        return Z_DATA_ERROR;
kusano fc6ab3
    }
kusano fc6ab3
    if (max == 9)                           /* 9 doesn't really mean 9 */
kusano fc6ab3
        max = 10;
kusano fc6ab3
    flags &= 0x80;                          /* true if block compress */
kusano fc6ab3
kusano fc6ab3
    /* clear table */
kusano fc6ab3
    bits = 9;
kusano fc6ab3
    mask = 0x1ff;
kusano fc6ab3
    end = flags ? 256 : 255;
kusano fc6ab3
kusano fc6ab3
    /* set up: get first 9-bit code, which is the first decompressed byte, but
kusano fc6ab3
       don't create a table entry until the next code */
kusano fc6ab3
    if (NEXT() == -1)                       /* no compressed data is ok */
kusano fc6ab3
        return Z_OK;
kusano fc6ab3
    final = prev = (unsigned)last;          /* low 8 bits of code */
kusano fc6ab3
    if (NEXT() == -1)                       /* missing a bit */
kusano fc6ab3
        return Z_BUF_ERROR;
kusano fc6ab3
    if (last & 1) {                         /* code must be < 256 */
kusano fc6ab3
        strm->msg = (char *)"invalid lzw code";
kusano fc6ab3
        return Z_DATA_ERROR;
kusano fc6ab3
    }
kusano fc6ab3
    rem = (unsigned)last >> 1;              /* remaining 7 bits */
kusano fc6ab3
    left = 7;
kusano fc6ab3
    chunk = bits - 2;                       /* 7 bytes left in this chunk */
kusano fc6ab3
    outbuf[0] = (unsigned char)final;       /* write first decompressed byte */
kusano fc6ab3
    outcnt = 1;
kusano fc6ab3
kusano fc6ab3
    /* decode codes */
kusano fc6ab3
    stack = 0;
kusano fc6ab3
    for (;;) {
kusano fc6ab3
        /* if the table will be full after this, increment the code size */
kusano fc6ab3
        if (end >= mask && bits < max) {
kusano fc6ab3
            FLUSHCODE();
kusano fc6ab3
            bits++;
kusano fc6ab3
            mask <<= 1;
kusano fc6ab3
            mask++;
kusano fc6ab3
        }
kusano fc6ab3
kusano fc6ab3
        /* get a code of length bits */
kusano fc6ab3
        if (chunk == 0)                     /* decrement chunk modulo bits */
kusano fc6ab3
            chunk = bits;
kusano fc6ab3
        code = rem;                         /* low bits of code */
kusano fc6ab3
        if (NEXT() == -1) {                 /* EOF is end of compressed data */
kusano fc6ab3
            /* write remaining buffered output */
kusano fc6ab3
            if (outcnt && out(&outd, outbuf, outcnt)) {
kusano fc6ab3
                strm->next_in = outbuf;     /* signal write error */
kusano fc6ab3
                return Z_BUF_ERROR;
kusano fc6ab3
            }
kusano fc6ab3
            return Z_OK;
kusano fc6ab3
        }
kusano fc6ab3
        code += (unsigned)last << left;     /* middle (or high) bits of code */
kusano fc6ab3
        left += 8;
kusano fc6ab3
        chunk--;
kusano fc6ab3
        if (bits > left) {                  /* need more bits */
kusano fc6ab3
            if (NEXT() == -1)               /* can't end in middle of code */
kusano fc6ab3
                return Z_BUF_ERROR;
kusano fc6ab3
            code += (unsigned)last << left; /* high bits of code */
kusano fc6ab3
            left += 8;
kusano fc6ab3
            chunk--;
kusano fc6ab3
        }
kusano fc6ab3
        code &= mask;                       /* mask to current code length */
kusano fc6ab3
        left -= bits;                       /* number of unused bits */
kusano fc6ab3
        rem = (unsigned)last >> (8 - left); /* unused bits from last byte */
kusano fc6ab3
kusano fc6ab3
        /* process clear code (256) */
kusano fc6ab3
        if (code == 256 && flags) {
kusano fc6ab3
            FLUSHCODE();
kusano fc6ab3
            bits = 9;                       /* initialize bits and mask */
kusano fc6ab3
            mask = 0x1ff;
kusano fc6ab3
            end = 255;                      /* empty table */
kusano fc6ab3
            continue;                       /* get next code */
kusano fc6ab3
        }
kusano fc6ab3
kusano fc6ab3
        /* special code to reuse last match */
kusano fc6ab3
        temp = code;                        /* save the current code */
kusano fc6ab3
        if (code > end) {
kusano fc6ab3
            /* Be picky on the allowed code here, and make sure that the code
kusano fc6ab3
               we drop through (prev) will be a valid index so that random
kusano fc6ab3
               input does not cause an exception.  The code != end + 1 check is
kusano fc6ab3
               empirically derived, and not checked in the original uncompress
kusano fc6ab3
               code.  If this ever causes a problem, that check could be safely
kusano fc6ab3
               removed.  Leaving this check in greatly improves gun's ability
kusano fc6ab3
               to detect random or corrupted input after a compress header.
kusano fc6ab3
               In any case, the prev > end check must be retained. */
kusano fc6ab3
            if (code != end + 1 || prev > end) {
kusano fc6ab3
                strm->msg = (char *)"invalid lzw code";
kusano fc6ab3
                return Z_DATA_ERROR;
kusano fc6ab3
            }
kusano fc6ab3
            match[stack++] = (unsigned char)final;
kusano fc6ab3
            code = prev;
kusano fc6ab3
        }
kusano fc6ab3
kusano fc6ab3
        /* walk through linked list to generate output in reverse order */
kusano fc6ab3
        p = match + stack;
kusano fc6ab3
        while (code >= 256) {
kusano fc6ab3
            *p++ = suffix[code];
kusano fc6ab3
            code = prefix[code];
kusano fc6ab3
        }
kusano fc6ab3
        stack = p - match;
kusano fc6ab3
        match[stack++] = (unsigned char)code;
kusano fc6ab3
        final = code;
kusano fc6ab3
kusano fc6ab3
        /* link new table entry */
kusano fc6ab3
        if (end < mask) {
kusano fc6ab3
            end++;
kusano fc6ab3
            prefix[end] = (unsigned short)prev;
kusano fc6ab3
            suffix[end] = (unsigned char)final;
kusano fc6ab3
        }
kusano fc6ab3
kusano fc6ab3
        /* set previous code for next iteration */
kusano fc6ab3
        prev = temp;
kusano fc6ab3
kusano fc6ab3
        /* write output in forward order */
kusano fc6ab3
        while (stack > SIZE - outcnt) {
kusano fc6ab3
            while (outcnt < SIZE)
kusano fc6ab3
                outbuf[outcnt++] = match[--stack];
kusano fc6ab3
            if (out(&outd, outbuf, outcnt)) {
kusano fc6ab3
                strm->next_in = outbuf; /* signal write error */
kusano fc6ab3
                return Z_BUF_ERROR;
kusano fc6ab3
            }
kusano fc6ab3
            outcnt = 0;
kusano fc6ab3
        }
kusano fc6ab3
        p = match + stack;
kusano fc6ab3
        do {
kusano fc6ab3
            outbuf[outcnt++] = *--p;
kusano fc6ab3
        } while (p > match);
kusano fc6ab3
        stack = 0;
kusano fc6ab3
kusano fc6ab3
        /* loop for next code with final and prev as the last match, rem and
kusano fc6ab3
           left provide the first 0..7 bits of the next code, end is the last
kusano fc6ab3
           valid table entry */
kusano fc6ab3
    }
kusano fc6ab3
}
kusano fc6ab3
kusano fc6ab3
/* Decompress a gzip file from infile to outfile.  strm is assumed to have been
kusano fc6ab3
   successfully initialized with inflateBackInit().  The input file may consist
kusano fc6ab3
   of a series of gzip streams, in which case all of them will be decompressed
kusano fc6ab3
   to the output file.  If outfile is -1, then the gzip stream(s) integrity is
kusano fc6ab3
   checked and nothing is written.
kusano fc6ab3
kusano fc6ab3
   The return value is a zlib error code: Z_MEM_ERROR if out of memory,
kusano fc6ab3
   Z_DATA_ERROR if the header or the compressed data is invalid, or if the
kusano fc6ab3
   trailer CRC-32 check or length doesn't match, Z_BUF_ERROR if the input ends
kusano fc6ab3
   prematurely or a write error occurs, or Z_ERRNO if junk (not a another gzip
kusano fc6ab3
   stream) follows a valid gzip stream.
kusano fc6ab3
 */
kusano fc6ab3
local int gunpipe(z_stream *strm, int infile, int outfile)
kusano fc6ab3
{
kusano fc6ab3
    int ret, first, last;
kusano fc6ab3
    unsigned have, flags, len;
kusano fc6ab3
    z_const unsigned char *next = NULL;
kusano fc6ab3
    struct ind ind, *indp;
kusano fc6ab3
    struct outd outd;
kusano fc6ab3
kusano fc6ab3
    /* setup input buffer */
kusano fc6ab3
    ind.infile = infile;
kusano fc6ab3
    ind.inbuf = inbuf;
kusano fc6ab3
    indp = &ind;
kusano fc6ab3
kusano fc6ab3
    /* decompress concatenated gzip streams */
kusano fc6ab3
    have = 0;                               /* no input data read in yet */
kusano fc6ab3
    first = 1;                              /* looking for first gzip header */
kusano fc6ab3
    strm->next_in = Z_NULL;                 /* so Z_BUF_ERROR means EOF */
kusano fc6ab3
    for (;;) {
kusano fc6ab3
        /* look for the two magic header bytes for a gzip stream */
kusano fc6ab3
        if (NEXT() == -1) {
kusano fc6ab3
            ret = Z_OK;
kusano fc6ab3
            break;                          /* empty gzip stream is ok */
kusano fc6ab3
        }
kusano fc6ab3
        if (last != 31 || (NEXT() != 139 && last != 157)) {
kusano fc6ab3
            strm->msg = (char *)"incorrect header check";
kusano fc6ab3
            ret = first ? Z_DATA_ERROR : Z_ERRNO;
kusano fc6ab3
            break;                          /* not a gzip or compress header */
kusano fc6ab3
        }
kusano fc6ab3
        first = 0;                          /* next non-header is junk */
kusano fc6ab3
kusano fc6ab3
        /* process a compress (LZW) file -- can't be concatenated after this */
kusano fc6ab3
        if (last == 157) {
kusano fc6ab3
            ret = lunpipe(have, next, indp, outfile, strm);
kusano fc6ab3
            break;
kusano fc6ab3
        }
kusano fc6ab3
kusano fc6ab3
        /* process remainder of gzip header */
kusano fc6ab3
        ret = Z_BUF_ERROR;
kusano fc6ab3
        if (NEXT() != 8) {                  /* only deflate method allowed */
kusano fc6ab3
            if (last == -1) break;
kusano fc6ab3
            strm->msg = (char *)"unknown compression method";
kusano fc6ab3
            ret = Z_DATA_ERROR;
kusano fc6ab3
            break;
kusano fc6ab3
        }
kusano fc6ab3
        flags = NEXT();                     /* header flags */
kusano fc6ab3
        NEXT();                             /* discard mod time, xflgs, os */
kusano fc6ab3
        NEXT();
kusano fc6ab3
        NEXT();
kusano fc6ab3
        NEXT();
kusano fc6ab3
        NEXT();
kusano fc6ab3
        NEXT();
kusano fc6ab3
        if (last == -1) break;
kusano fc6ab3
        if (flags & 0xe0) {
kusano fc6ab3
            strm->msg = (char *)"unknown header flags set";
kusano fc6ab3
            ret = Z_DATA_ERROR;
kusano fc6ab3
            break;
kusano fc6ab3
        }
kusano fc6ab3
        if (flags & 4) {                    /* extra field */
kusano fc6ab3
            len = NEXT();
kusano fc6ab3
            len += (unsigned)(NEXT()) << 8;
kusano fc6ab3
            if (last == -1) break;
kusano fc6ab3
            while (len > have) {
kusano fc6ab3
                len -= have;
kusano fc6ab3
                have = 0;
kusano fc6ab3
                if (NEXT() == -1) break;
kusano fc6ab3
                len--;
kusano fc6ab3
            }
kusano fc6ab3
            if (last == -1) break;
kusano fc6ab3
            have -= len;
kusano fc6ab3
            next += len;
kusano fc6ab3
        }
kusano fc6ab3
        if (flags & 8)                      /* file name */
kusano fc6ab3
            while (NEXT() != 0 && last != -1)
kusano fc6ab3
                ;
kusano fc6ab3
        if (flags & 16)                     /* comment */
kusano fc6ab3
            while (NEXT() != 0 && last != -1)
kusano fc6ab3
                ;
kusano fc6ab3
        if (flags & 2) {                    /* header crc */
kusano fc6ab3
            NEXT();
kusano fc6ab3
            NEXT();
kusano fc6ab3
        }
kusano fc6ab3
        if (last == -1) break;
kusano fc6ab3
kusano fc6ab3
        /* set up output */
kusano fc6ab3
        outd.outfile = outfile;
kusano fc6ab3
        outd.check = 1;
kusano fc6ab3
        outd.crc = crc32(0L, Z_NULL, 0);
kusano fc6ab3
        outd.total = 0;
kusano fc6ab3
kusano fc6ab3
        /* decompress data to output */
kusano fc6ab3
        strm->next_in = next;
kusano fc6ab3
        strm->avail_in = have;
kusano fc6ab3
        ret = inflateBack(strm, in, indp, out, &outd);
kusano fc6ab3
        if (ret != Z_STREAM_END) break;
kusano fc6ab3
        next = strm->next_in;
kusano fc6ab3
        have = strm->avail_in;
kusano fc6ab3
        strm->next_in = Z_NULL;             /* so Z_BUF_ERROR means EOF */
kusano fc6ab3
kusano fc6ab3
        /* check trailer */
kusano fc6ab3
        ret = Z_BUF_ERROR;
kusano fc6ab3
        if (NEXT() != (int)(outd.crc & 0xff) ||
kusano fc6ab3
            NEXT() != (int)((outd.crc >> 8) & 0xff) ||
kusano fc6ab3
            NEXT() != (int)((outd.crc >> 16) & 0xff) ||
kusano fc6ab3
            NEXT() != (int)((outd.crc >> 24) & 0xff)) {
kusano fc6ab3
            /* crc error */
kusano fc6ab3
            if (last != -1) {
kusano fc6ab3
                strm->msg = (char *)"incorrect data check";
kusano fc6ab3
                ret = Z_DATA_ERROR;
kusano fc6ab3
            }
kusano fc6ab3
            break;
kusano fc6ab3
        }
kusano fc6ab3
        if (NEXT() != (int)(outd.total & 0xff) ||
kusano fc6ab3
            NEXT() != (int)((outd.total >> 8) & 0xff) ||
kusano fc6ab3
            NEXT() != (int)((outd.total >> 16) & 0xff) ||
kusano fc6ab3
            NEXT() != (int)((outd.total >> 24) & 0xff)) {
kusano fc6ab3
            /* length error */
kusano fc6ab3
            if (last != -1) {
kusano fc6ab3
                strm->msg = (char *)"incorrect length check";
kusano fc6ab3
                ret = Z_DATA_ERROR;
kusano fc6ab3
            }
kusano fc6ab3
            break;
kusano fc6ab3
        }
kusano fc6ab3
kusano fc6ab3
        /* go back and look for another gzip stream */
kusano fc6ab3
    }
kusano fc6ab3
kusano fc6ab3
    /* clean up and return */
kusano fc6ab3
    return ret;
kusano fc6ab3
}
kusano fc6ab3
kusano fc6ab3
/* Copy file attributes, from -> to, as best we can.  This is best effort, so
kusano fc6ab3
   no errors are reported.  The mode bits, including suid, sgid, and the sticky
kusano fc6ab3
   bit are copied (if allowed), the owner's user id and group id are copied
kusano fc6ab3
   (again if allowed), and the access and modify times are copied. */
kusano fc6ab3
local void copymeta(char *from, char *to)
kusano fc6ab3
{
kusano fc6ab3
    struct stat was;
kusano fc6ab3
    struct utimbuf when;
kusano fc6ab3
kusano fc6ab3
    /* get all of from's Unix meta data, return if not a regular file */
kusano fc6ab3
    if (stat(from, &was) != 0 || (was.st_mode & S_IFMT) != S_IFREG)
kusano fc6ab3
        return;
kusano fc6ab3
kusano fc6ab3
    /* set to's mode bits, ignore errors */
kusano fc6ab3
    (void)chmod(to, was.st_mode & 07777);
kusano fc6ab3
kusano fc6ab3
    /* copy owner's user and group, ignore errors */
kusano fc6ab3
    (void)chown(to, was.st_uid, was.st_gid);
kusano fc6ab3
kusano fc6ab3
    /* copy access and modify times, ignore errors */
kusano fc6ab3
    when.actime = was.st_atime;
kusano fc6ab3
    when.modtime = was.st_mtime;
kusano fc6ab3
    (void)utime(to, &when);
kusano fc6ab3
}
kusano fc6ab3
kusano fc6ab3
/* Decompress the file inname to the file outnname, of if test is true, just
kusano fc6ab3
   decompress without writing and check the gzip trailer for integrity.  If
kusano fc6ab3
   inname is NULL or an empty string, read from stdin.  If outname is NULL or
kusano fc6ab3
   an empty string, write to stdout.  strm is a pre-initialized inflateBack
kusano fc6ab3
   structure.  When appropriate, copy the file attributes from inname to
kusano fc6ab3
   outname.
kusano fc6ab3
kusano fc6ab3
   gunzip() returns 1 if there is an out-of-memory error or an unexpected
kusano fc6ab3
   return code from gunpipe().  Otherwise it returns 0.
kusano fc6ab3
 */
kusano fc6ab3
local int gunzip(z_stream *strm, char *inname, char *outname, int test)
kusano fc6ab3
{
kusano fc6ab3
    int ret;
kusano fc6ab3
    int infile, outfile;
kusano fc6ab3
kusano fc6ab3
    /* open files */
kusano fc6ab3
    if (inname == NULL || *inname == 0) {
kusano fc6ab3
        inname = "-";
kusano fc6ab3
        infile = 0;     /* stdin */
kusano fc6ab3
    }
kusano fc6ab3
    else {
kusano fc6ab3
        infile = open(inname, O_RDONLY, 0);
kusano fc6ab3
        if (infile == -1) {
kusano fc6ab3
            fprintf(stderr, "gun cannot open %s\n", inname);
kusano fc6ab3
            return 0;
kusano fc6ab3
        }
kusano fc6ab3
    }
kusano fc6ab3
    if (test)
kusano fc6ab3
        outfile = -1;
kusano fc6ab3
    else if (outname == NULL || *outname == 0) {
kusano fc6ab3
        outname = "-";
kusano fc6ab3
        outfile = 1;    /* stdout */
kusano fc6ab3
    }
kusano fc6ab3
    else {
kusano fc6ab3
        outfile = open(outname, O_CREAT | O_TRUNC | O_WRONLY, 0666);
kusano fc6ab3
        if (outfile == -1) {
kusano fc6ab3
            close(infile);
kusano fc6ab3
            fprintf(stderr, "gun cannot create %s\n", outname);
kusano fc6ab3
            return 0;
kusano fc6ab3
        }
kusano fc6ab3
    }
kusano fc6ab3
    errno = 0;
kusano fc6ab3
kusano fc6ab3
    /* decompress */
kusano fc6ab3
    ret = gunpipe(strm, infile, outfile);
kusano fc6ab3
    if (outfile > 2) close(outfile);
kusano fc6ab3
    if (infile > 2) close(infile);
kusano fc6ab3
kusano fc6ab3
    /* interpret result */
kusano fc6ab3
    switch (ret) {
kusano fc6ab3
    case Z_OK:
kusano fc6ab3
    case Z_ERRNO:
kusano fc6ab3
        if (infile > 2 && outfile > 2) {
kusano fc6ab3
            copymeta(inname, outname);          /* copy attributes */
kusano fc6ab3
            unlink(inname);
kusano fc6ab3
        }
kusano fc6ab3
        if (ret == Z_ERRNO)
kusano fc6ab3
            fprintf(stderr, "gun warning: trailing garbage ignored in %s\n",
kusano fc6ab3
                    inname);
kusano fc6ab3
        break;
kusano fc6ab3
    case Z_DATA_ERROR:
kusano fc6ab3
        if (outfile > 2) unlink(outname);
kusano fc6ab3
        fprintf(stderr, "gun data error on %s: %s\n", inname, strm->msg);
kusano fc6ab3
        break;
kusano fc6ab3
    case Z_MEM_ERROR:
kusano fc6ab3
        if (outfile > 2) unlink(outname);
kusano fc6ab3
        fprintf(stderr, "gun out of memory error--aborting\n");
kusano fc6ab3
        return 1;
kusano fc6ab3
    case Z_BUF_ERROR:
kusano fc6ab3
        if (outfile > 2) unlink(outname);
kusano fc6ab3
        if (strm->next_in != Z_NULL) {
kusano fc6ab3
            fprintf(stderr, "gun write error on %s: %s\n",
kusano fc6ab3
                    outname, strerror(errno));
kusano fc6ab3
        }
kusano fc6ab3
        else if (errno) {
kusano fc6ab3
            fprintf(stderr, "gun read error on %s: %s\n",
kusano fc6ab3
                    inname, strerror(errno));
kusano fc6ab3
        }
kusano fc6ab3
        else {
kusano fc6ab3
            fprintf(stderr, "gun unexpected end of file on %s\n",
kusano fc6ab3
                    inname);
kusano fc6ab3
        }
kusano fc6ab3
        break;
kusano fc6ab3
    default:
kusano fc6ab3
        if (outfile > 2) unlink(outname);
kusano fc6ab3
        fprintf(stderr, "gun internal error--aborting\n");
kusano fc6ab3
        return 1;
kusano fc6ab3
    }
kusano fc6ab3
    return 0;
kusano fc6ab3
}
kusano fc6ab3
kusano fc6ab3
/* Process the gun command line arguments.  See the command syntax near the
kusano fc6ab3
   beginning of this source file. */
kusano fc6ab3
int main(int argc, char **argv)
kusano fc6ab3
{
kusano fc6ab3
    int ret, len, test;
kusano fc6ab3
    char *outname;
kusano fc6ab3
    unsigned char *window;
kusano fc6ab3
    z_stream strm;
kusano fc6ab3
kusano fc6ab3
    /* initialize inflateBack state for repeated use */
kusano fc6ab3
    window = match;                         /* reuse LZW match buffer */
kusano fc6ab3
    strm.zalloc = Z_NULL;
kusano fc6ab3
    strm.zfree = Z_NULL;
kusano fc6ab3
    strm.opaque = Z_NULL;
kusano fc6ab3
    ret = inflateBackInit(&strm, 15, window);
kusano fc6ab3
    if (ret != Z_OK) {
kusano fc6ab3
        fprintf(stderr, "gun out of memory error--aborting\n");
kusano fc6ab3
        return 1;
kusano fc6ab3
    }
kusano fc6ab3
kusano fc6ab3
    /* decompress each file to the same name with the suffix removed */
kusano fc6ab3
    argc--;
kusano fc6ab3
    argv++;
kusano fc6ab3
    test = 0;
kusano fc6ab3
    if (argc && strcmp(*argv, "-h") == 0) {
kusano fc6ab3
        fprintf(stderr, "gun 1.6 (17 Jan 2010)\n");
kusano fc6ab3
        fprintf(stderr, "Copyright (C) 2003-2010 Mark Adler\n");
kusano fc6ab3
        fprintf(stderr, "usage: gun [-t] [file1.gz [file2.Z ...]]\n");
kusano fc6ab3
        return 0;
kusano fc6ab3
    }
kusano fc6ab3
    if (argc && strcmp(*argv, "-t") == 0) {
kusano fc6ab3
        test = 1;
kusano fc6ab3
        argc--;
kusano fc6ab3
        argv++;
kusano fc6ab3
    }
kusano fc6ab3
    if (argc)
kusano fc6ab3
        do {
kusano fc6ab3
            if (test)
kusano fc6ab3
                outname = NULL;
kusano fc6ab3
            else {
kusano fc6ab3
                len = (int)strlen(*argv);
kusano fc6ab3
                if (strcmp(*argv + len - 3, ".gz") == 0 ||
kusano fc6ab3
                    strcmp(*argv + len - 3, "-gz") == 0)
kusano fc6ab3
                    len -= 3;
kusano fc6ab3
                else if (strcmp(*argv + len - 2, ".z") == 0 ||
kusano fc6ab3
                    strcmp(*argv + len - 2, "-z") == 0 ||
kusano fc6ab3
                    strcmp(*argv + len - 2, "_z") == 0 ||
kusano fc6ab3
                    strcmp(*argv + len - 2, ".Z") == 0)
kusano fc6ab3
                    len -= 2;
kusano fc6ab3
                else {
kusano fc6ab3
                    fprintf(stderr, "gun error: no gz type on %s--skipping\n",
kusano fc6ab3
                            *argv);
kusano fc6ab3
                    continue;
kusano fc6ab3
                }
kusano fc6ab3
                outname = malloc(len + 1);
kusano fc6ab3
                if (outname == NULL) {
kusano fc6ab3
                    fprintf(stderr, "gun out of memory error--aborting\n");
kusano fc6ab3
                    ret = 1;
kusano fc6ab3
                    break;
kusano fc6ab3
                }
kusano fc6ab3
                memcpy(outname, *argv, len);
kusano fc6ab3
                outname[len] = 0;
kusano fc6ab3
            }
kusano fc6ab3
            ret = gunzip(&strm, *argv, outname, test);
kusano fc6ab3
            if (outname != NULL) free(outname);
kusano fc6ab3
            if (ret) break;
kusano fc6ab3
        } while (argv++, --argc);
kusano fc6ab3
    else
kusano fc6ab3
        ret = gunzip(&strm, NULL, NULL, test);
kusano fc6ab3
kusano fc6ab3
    /* clean up */
kusano fc6ab3
    inflateBackEnd(&strm);
kusano fc6ab3
    return ret;
kusano fc6ab3
}