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