xref: /third_party/zlib/test/minigzip.c (revision 275793ea)
1/* minigzip.c -- simulate gzip using the zlib compression library
2 * Copyright (C) 1995-2006, 2010, 2011, 2016 Jean-loup Gailly
3 * For conditions of distribution and use, see copyright notice in zlib.h
4 */
5
6/*
7 * minigzip is a minimal implementation of the gzip utility. This is
8 * only an example of using zlib and isn't meant to replace the
9 * full-featured gzip. No attempt is made to deal with file systems
10 * limiting names to 14 or 8+3 characters, etc... Error checking is
11 * very limited. So use minigzip only for testing; use gzip for the
12 * real thing. On MSDOS, use only on file names without extension
13 * or in pipe mode.
14 */
15
16/* @(#) $Id$ */
17
18#include "zlib.h"
19#include <stdio.h>
20
21#ifdef STDC
22#  include <string.h>
23#  include <stdlib.h>
24#endif
25
26#ifdef USE_MMAP
27#  include <sys/types.h>
28#  include <sys/mman.h>
29#  include <sys/stat.h>
30#endif
31
32#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
33#  include <fcntl.h>
34#  include <io.h>
35#  ifdef UNDER_CE
36#    include <stdlib.h>
37#  endif
38#  define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
39#else
40#  define SET_BINARY_MODE(file)
41#endif
42
43#if defined(_MSC_VER) && _MSC_VER < 1900
44#  define snprintf _snprintf
45#endif
46
47#ifdef VMS
48#  define unlink delete
49#  define GZ_SUFFIX "-gz"
50#endif
51#ifdef RISCOS
52#  define unlink remove
53#  define GZ_SUFFIX "-gz"
54#  define fileno(file) file->__file
55#endif
56#if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
57#  include <unix.h> /* for fileno */
58#endif
59
60#if !defined(Z_HAVE_UNISTD_H) && !defined(_LARGEFILE64_SOURCE)
61#ifndef WIN32 /* unlink already in stdio.h for WIN32 */
62  extern int unlink(const char *);
63#endif
64#endif
65
66#if defined(UNDER_CE)
67#  include <windows.h>
68#  define perror(s) pwinerror(s)
69
70/* Map the Windows error number in ERROR to a locale-dependent error
71   message string and return a pointer to it.  Typically, the values
72   for ERROR come from GetLastError.
73
74   The string pointed to shall not be modified by the application,
75   but may be overwritten by a subsequent call to strwinerror
76
77   The strwinerror function does not change the current setting
78   of GetLastError.  */
79
80static char *strwinerror (error)
81     DWORD error;
82{
83    static char buf[1024];
84
85    wchar_t *msgbuf;
86    DWORD lasterr = GetLastError();
87    DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
88        | FORMAT_MESSAGE_ALLOCATE_BUFFER,
89        NULL,
90        error,
91        0, /* Default language */
92        (LPVOID)&msgbuf,
93        0,
94        NULL);
95    if (chars != 0) {
96        /* If there is an \r\n appended, zap it.  */
97        if (chars >= 2
98            && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') {
99            chars -= 2;
100            msgbuf[chars] = 0;
101        }
102
103        if (chars > sizeof (buf) - 1) {
104            chars = sizeof (buf) - 1;
105            msgbuf[chars] = 0;
106        }
107
108        wcstombs(buf, msgbuf, chars + 1);
109        LocalFree(msgbuf);
110    }
111    else {
112        sprintf(buf, "unknown win32 error (%ld)", error);
113    }
114
115    SetLastError(lasterr);
116    return buf;
117}
118
119static void pwinerror (s)
120    const char *s;
121{
122    if (s && *s)
123        fprintf(stderr, "%s: %s\n", s, strwinerror(GetLastError ()));
124    else
125        fprintf(stderr, "%s\n", strwinerror(GetLastError ()));
126}
127
128#endif /* UNDER_CE */
129
130#ifndef GZ_SUFFIX
131#  define GZ_SUFFIX ".gz"
132#endif
133#define SUFFIX_LEN (sizeof(GZ_SUFFIX)-1)
134
135#define BUFLEN      16384
136#define MAX_NAME_LEN 1024
137
138#ifdef MAXSEG_64K
139#  define local static
140   /* Needed for systems with limitation on stack size. */
141#else
142#  define local
143#endif
144
145#ifdef Z_SOLO
146/* for Z_SOLO, create simplified gz* functions using deflate and inflate */
147
148#if defined(Z_HAVE_UNISTD_H) || defined(Z_LARGE)
149#  include <unistd.h>       /* for unlink() */
150#endif
151
152static void *myalloc(void *q, unsigned n, unsigned m)
153{
154    (void)q;
155    return calloc(n, m);
156}
157
158static void myfree(void *q, void *p)
159{
160    (void)q;
161    free(p);
162}
163
164typedef struct gzFile_s {
165    FILE *file;
166    int write;
167    int err;
168    char *msg;
169    z_stream strm;
170} *gzFile;
171
172static gzFile gz_open(const char *path, int fd, const char *mode)
173{
174    gzFile gz;
175    int ret;
176
177    gz = malloc(sizeof(struct gzFile_s));
178    if (gz == NULL)
179        return NULL;
180    gz->write = strchr(mode, 'w') != NULL;
181    gz->strm.zalloc = myalloc;
182    gz->strm.zfree = myfree;
183    gz->strm.opaque = Z_NULL;
184    if (gz->write)
185        ret = deflateInit2(&(gz->strm), -1, 8, 15 + 16, 8, 0);
186    else {
187        gz->strm.next_in = 0;
188        gz->strm.avail_in = Z_NULL;
189        ret = inflateInit2(&(gz->strm), 15 + 16);
190    }
191    if (ret != Z_OK) {
192        free(gz);
193        return NULL;
194    }
195    gz->file = path == NULL ? fdopen(fd, gz->write ? "wb" : "rb") :
196                              fopen(path, gz->write ? "wb" : "rb");
197    if (gz->file == NULL) {
198        gz->write ? deflateEnd(&(gz->strm)) : inflateEnd(&(gz->strm));
199        free(gz);
200        return NULL;
201    }
202    gz->err = 0;
203    gz->msg = "";
204    return gz;
205}
206
207static gzFile gzopen(const char *path, const char *mode)
208{
209    return gz_open(path, -1, mode);
210}
211
212static gzFile gzdopen(int fd, const char *mode)
213{
214    return gz_open(NULL, fd, mode);
215}
216
217static int gzwrite(gzFile gz, const void *buf, unsigned len)
218{
219    z_stream *strm;
220    unsigned char out[BUFLEN];
221
222    if (gz == NULL || !gz->write)
223        return 0;
224    strm = &(gz->strm);
225    strm->next_in = (void *)buf;
226    strm->avail_in = len;
227    do {
228        strm->next_out = out;
229        strm->avail_out = BUFLEN;
230        (void)deflate(strm, Z_NO_FLUSH);
231        fwrite(out, 1, BUFLEN - strm->avail_out, gz->file);
232    } while (strm->avail_out == 0);
233    return len;
234}
235
236static int gzread(gzFile gz, void *buf, unsigned len)
237{
238    int ret;
239    unsigned got;
240    unsigned char in[1];
241    z_stream *strm;
242
243    if (gz == NULL || gz->write)
244        return 0;
245    if (gz->err)
246        return 0;
247    strm = &(gz->strm);
248    strm->next_out = (void *)buf;
249    strm->avail_out = len;
250    do {
251        got = fread(in, 1, 1, gz->file);
252        if (got == 0)
253            break;
254        strm->next_in = in;
255        strm->avail_in = 1;
256        ret = inflate(strm, Z_NO_FLUSH);
257        if (ret == Z_DATA_ERROR) {
258            gz->err = Z_DATA_ERROR;
259            gz->msg = strm->msg;
260            return 0;
261        }
262        if (ret == Z_STREAM_END)
263            inflateReset(strm);
264    } while (strm->avail_out);
265    return len - strm->avail_out;
266}
267
268static int gzclose(gzFile gz)
269{
270    z_stream *strm;
271    unsigned char out[BUFLEN];
272
273    if (gz == NULL)
274        return Z_STREAM_ERROR;
275    strm = &(gz->strm);
276    if (gz->write) {
277        strm->next_in = Z_NULL;
278        strm->avail_in = 0;
279        do {
280            strm->next_out = out;
281            strm->avail_out = BUFLEN;
282            (void)deflate(strm, Z_FINISH);
283            fwrite(out, 1, BUFLEN - strm->avail_out, gz->file);
284        } while (strm->avail_out == 0);
285        deflateEnd(strm);
286    }
287    else
288        inflateEnd(strm);
289    fclose(gz->file);
290    free(gz);
291    return Z_OK;
292}
293
294static const char *gzerror(gzFile gz, int *err)
295{
296    *err = gz->err;
297    return gz->msg;
298}
299
300#endif
301
302static char *prog;
303
304/* ===========================================================================
305 * Display error message and exit
306 */
307static void error(const char *msg)
308{
309    fprintf(stderr, "%s: %s\n", prog, msg);
310    exit(1);
311}
312
313#ifdef USE_MMAP /* MMAP version, Miguel Albrecht <malbrech@eso.org> */
314
315/* Try compressing the input file at once using mmap. Return Z_OK if
316 * success, Z_ERRNO otherwise.
317 */
318static int gz_compress_mmap(FILE *in, gzFile out)
319{
320    int len;
321    int err;
322    int ifd = fileno(in);
323    caddr_t buf;    /* mmap'ed buffer for the entire input file */
324    off_t buf_len;  /* length of the input file */
325    struct stat sb;
326
327    /* Determine the size of the file, needed for mmap: */
328    if (fstat(ifd, &sb) < 0)
329    {
330        return Z_ERRNO;
331    }
332    buf_len = sb.st_size;
333    if (buf_len <= 0)
334    {
335        return Z_ERRNO;
336    }
337
338    /* Now do the actual mmap: */
339    buf = mmap((caddr_t) 0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0);
340    if (buf == (caddr_t)(-1))
341    {
342        return Z_ERRNO;
343    }
344
345    /* Compress the whole file at once: */
346    len = gzwrite(out, (char *)buf, (unsigned)buf_len);
347
348    if (len != (int)buf_len)
349    {
350        error(gzerror(out, &err));
351    }
352
353    munmap(buf, buf_len);
354    fclose(in);
355    if (gzclose(out) != Z_OK)
356    {
357        error("failed gzclose");
358    }
359    return Z_OK;
360}
361#endif /* USE_MMAP */
362
363/* ===========================================================================
364 * Compress input to output then close both files.
365 */
366
367static void gz_compress(FILE *in, gzFile out)
368{
369    local char buf[BUFLEN];
370    int len;
371    int err;
372
373#ifdef USE_MMAP
374    /* Try first compressing with mmap. If mmap fails (minigzip used in a
375     * pipe), use the normal fread loop.
376     */
377    if (gz_compress_mmap(in, out) == Z_OK) return;
378#endif
379    for (;;) {
380        len = (int)fread(buf, 1, sizeof(buf), in);
381        if (ferror(in)) {
382            perror("fread");
383            exit(1);
384        }
385        if (len == 0)
386        {
387            break;
388        }
389
390        if (gzwrite(out, buf, (unsigned)len) != len)
391        {
392            error(gzerror(out, &err));
393        }
394    }
395    fclose(in);
396    if (gzclose(out) != Z_OK)
397    {
398        error("failed gzclose");
399    }
400}
401
402/* ===========================================================================
403 * Uncompress input to output then close both files.
404 */
405static void gz_uncompress(gzFile in, FILE *out)
406{
407    local char buf[BUFLEN];
408    int len;
409    int err;
410
411    for (;;) {
412        len = gzread(in, buf, sizeof(buf));
413        if (len < 0)
414        {
415            error (gzerror(in, &err));
416        }
417        if (len == 0)
418        {
419            break;
420        }
421
422        if ((int)fwrite(buf, 1, (unsigned)len, out) != len) {
423            error("failed fwrite");
424        }
425    }
426    if (fclose(out))
427    {
428        error("failed fclose");
429    }
430
431    if (gzclose(in) != Z_OK)
432    {
433        error("failed gzclose");
434    }
435}
436
437
438/* ===========================================================================
439 * Compress the given file: create a corresponding .gz file and remove the
440 * original.
441 */
442static void file_compress(char *file, char *mode)
443{
444    local char outfile[MAX_NAME_LEN];
445    FILE  *in;
446    gzFile out;
447
448    if (strlen(file) + strlen(GZ_SUFFIX) >= sizeof(outfile)) {
449        fprintf(stderr, "%s: filename too long\n", prog);
450        exit(1);
451    }
452
453#if !defined(NO_snprintf) && !defined(NO_vsnprintf)
454    snprintf(outfile, sizeof(outfile), "%s%s", file, GZ_SUFFIX);
455#else
456    strcpy(outfile, file);
457    strcat(outfile, GZ_SUFFIX);
458#endif
459
460    in = fopen(file, "rb");
461    if (in == NULL) {
462        perror(file);
463        exit(1);
464    }
465    out = gzopen(outfile, mode);
466    if (out == NULL) {
467        fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile);
468        exit(1);
469    }
470    gz_compress(in, out);
471
472    unlink(file);
473}
474
475
476/* ===========================================================================
477 * Uncompress the given file and remove the original.
478 */
479static void file_uncompress(char *file)
480{
481    local char buf[MAX_NAME_LEN];
482    char *infile, *outfile;
483    FILE  *out;
484    gzFile in;
485    z_size_t len = strlen(file);
486
487    if (len + strlen(GZ_SUFFIX) >= sizeof(buf)) {
488        fprintf(stderr, "%s: filename too long\n", prog);
489        exit(1);
490    }
491
492#if !defined(NO_snprintf) && !defined(NO_vsnprintf)
493    snprintf(buf, sizeof(buf), "%s", file);
494#else
495    strcpy(buf, file);
496#endif
497
498    if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) {
499        infile = file;
500        outfile = buf;
501        outfile[len-3] = '\0';
502    } else {
503        outfile = file;
504        infile = buf;
505#if !defined(NO_snprintf) && !defined(NO_vsnprintf)
506        snprintf(buf + len, sizeof(buf) - len, "%s", GZ_SUFFIX);
507#else
508        strcat(infile, GZ_SUFFIX);
509#endif
510    }
511    in = gzopen(infile, "rb");
512    if (in == NULL) {
513        fprintf(stderr, "%s: can't gzopen %s\n", prog, infile);
514        exit(1);
515    }
516    out = fopen(outfile, "wb");
517    if (out == NULL) {
518        perror(file);
519        exit(1);
520    }
521
522    gz_uncompress(in, out);
523
524    unlink(infile);
525}
526
527
528/* ===========================================================================
529 * Usage:  minigzip [-c] [-d] [-f] [-h] [-r] [-1 to -9] [files...]
530 *   -c : write to standard output
531 *   -d : decompress
532 *   -f : compress with Z_FILTERED
533 *   -h : compress with Z_HUFFMAN_ONLY
534 *   -r : compress with Z_RLE
535 *   -1 to -9 : compression level
536 */
537
538int main(int argc, char *argv[])
539{
540    int copyout = 0;
541    int uncompr = 0;
542    gzFile file;
543    char *bname, outmode[20];
544
545#if !defined(NO_snprintf) && !defined(NO_vsnprintf)
546    snprintf(outmode, sizeof(outmode), "%s", "wb6 ");
547#else
548    strcpy(outmode, "wb6 ");
549#endif
550
551    prog = argv[0];
552    bname = strrchr(argv[0], '/');
553    if (bname)
554    {
555      bname++;
556    }
557    else
558    {
559      bname = argv[0];
560    }
561    argc--, argv++;
562
563    if (!strcmp(bname, "gunzip"))
564    {
565      uncompr = 1;
566    }
567    else if (!strcmp(bname, "zcat"))
568    {
569      copyout = uncompr = 1;
570    }
571
572    while (argc > 0) {
573      if (strcmp(*argv, "-c") == 0)
574      {
575        copyout = 1;
576      }
577      else if (strcmp(*argv, "-d") == 0)
578      {
579        uncompr = 1;
580      }
581      else if (strcmp(*argv, "-f") == 0)
582      {
583        outmode[3] = 'f';
584      }
585      else if (strcmp(*argv, "-h") == 0)
586      {
587        outmode[3] = 'h';
588      }
589      else if (strcmp(*argv, "-r") == 0)
590      {
591        outmode[3] = 'R';
592      }
593      else if ((*argv)[0] == '-' && (*argv)[1] >= '1' && (*argv)[1] <= '9' &&
594               (*argv)[2] == 0)
595      {
596        outmode[2] = (*argv)[1];
597      }
598      else
599      {
600        break;
601      }
602      argc--, argv++;
603    }
604    if (outmode[3] == ' ')
605    {
606        outmode[3] = 0;
607    }
608    if (argc == 0) {
609        SET_BINARY_MODE(stdin);
610        SET_BINARY_MODE(stdout);
611        if (uncompr) {
612            file = gzdopen(fileno(stdin), "rb");
613            if (file == NULL)
614            {
615                error("can't gzdopen stdin");
616            }
617            gz_uncompress(file, stdout);
618        } else {
619            file = gzdopen(fileno(stdout), outmode);
620            if (file == NULL)
621            {
622                error("can't gzdopen stdout");
623            }
624            gz_compress(stdin, file);
625        }
626    } else {
627        if (copyout) {
628            SET_BINARY_MODE(stdout);
629        }
630        do {
631            if (uncompr) {
632                if (copyout) {
633                    file = gzopen(*argv, "rb");
634                    if (file == NULL)
635                    {
636                        fprintf(stderr, "%s: can't gzopen %s\n", prog, *argv);
637                    }
638                    else
639                    {
640                        gz_uncompress(file, stdout);
641                    }
642                } else {
643                    file_uncompress(*argv);
644                }
645            } else {
646                if (copyout) {
647                    FILE * in = fopen(*argv, "rb");
648
649                    if (in == NULL) {
650                        perror(*argv);
651                    } else {
652                        file = gzdopen(fileno(stdout), outmode);
653                        if (file == NULL)
654                        {
655                            error("can't gzdopen stdout");
656                        }
657
658                        gz_compress(in, file);
659                    }
660
661                } else {
662                    file_compress(*argv, outmode);
663                }
664            }
665        } while (argv++, --argc);
666    }
667    return 0;
668}
669