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 orignal 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 */
81struct 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. */
89local 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.) */
119struct 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 */
131local 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 */
161unsigned char inbuf[SIZE];              /* input buffer */
162unsigned char outbuf[SIZE];             /* output buffer */
163unsigned short prefix[65536];           /* index to LZW prefix string */
164unsigned char suffix[65536];            /* one-character LZW suffix */
165unsigned 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 */
200local 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 */
383local 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. */
517local 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 */
548local 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. */
631int 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}
703