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