"Fossies" - the Fresh Open Source Software Archive

Member "muscle/zlib/zlib/examples/gun.c" (21 Nov 2020, 25943 Bytes) of package /linux/privat/muscle7.62.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "gun.c": 7.61_vs_7.62.

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