Home | History | Annotate | Download | only in zlib
      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 _MSC_VER
     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 OF((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 
     80 static 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 
    119 static 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 
    152 void *myalloc OF((void *, unsigned, unsigned));
    153 void myfree OF((void *, void *));
    154 
    155 void *myalloc(q, n, m)
    156     void *q;
    157     unsigned n, m;
    158 {
    159     q = Z_NULL;
    160     return calloc(n, m);
    161 }
    162 
    163 void myfree(q, p)
    164     void *q, *p;
    165 {
    166     q = Z_NULL;
    167     free(p);
    168 }
    169 
    170 typedef struct gzFile_s {
    171     FILE *file;
    172     int write;
    173     int err;
    174     char *msg;
    175     z_stream strm;
    176 } *gzFile;
    177 
    178 gzFile gzopen OF((const char *, const char *));
    179 gzFile gzdopen OF((int, const char *));
    180 gzFile gz_open OF((const char *, int, const char *));
    181 
    182 gzFile gzopen(path, mode)
    183 const char *path;
    184 const char *mode;
    185 {
    186     return gz_open(path, -1, mode);
    187 }
    188 
    189 gzFile gzdopen(fd, mode)
    190 int fd;
    191 const char *mode;
    192 {
    193     return gz_open(NULL, fd, mode);
    194 }
    195 
    196 gzFile gz_open(path, fd, mode)
    197     const char *path;
    198     int fd;
    199     const char *mode;
    200 {
    201     gzFile gz;
    202     int ret;
    203 
    204     gz = malloc(sizeof(struct gzFile_s));
    205     if (gz == NULL)
    206         return NULL;
    207     gz->write = strchr(mode, 'w') != NULL;
    208     gz->strm.zalloc = myalloc;
    209     gz->strm.zfree = myfree;
    210     gz->strm.opaque = Z_NULL;
    211     if (gz->write)
    212         ret = deflateInit2(&(gz->strm), -1, 8, 15 + 16, 8, 0);
    213     else {
    214         gz->strm.next_in = 0;
    215         gz->strm.avail_in = Z_NULL;
    216         ret = inflateInit2(&(gz->strm), 15 + 16);
    217     }
    218     if (ret != Z_OK) {
    219         free(gz);
    220         return NULL;
    221     }
    222     gz->file = path == NULL ? fdopen(fd, gz->write ? "wb" : "rb") :
    223                               fopen(path, gz->write ? "wb" : "rb");
    224     if (gz->file == NULL) {
    225         gz->write ? deflateEnd(&(gz->strm)) : inflateEnd(&(gz->strm));
    226         free(gz);
    227         return NULL;
    228     }
    229     gz->err = 0;
    230     gz->msg = "";
    231     return gz;
    232 }
    233 
    234 int gzwrite OF((gzFile, const void *, unsigned));
    235 
    236 int gzwrite(gz, buf, len)
    237     gzFile gz;
    238     const void *buf;
    239     unsigned len;
    240 {
    241     z_stream *strm;
    242     unsigned char out[BUFLEN];
    243 
    244     if (gz == NULL || !gz->write)
    245         return 0;
    246     strm = &(gz->strm);
    247     strm->next_in = (void *)buf;
    248     strm->avail_in = len;
    249     do {
    250         strm->next_out = out;
    251         strm->avail_out = BUFLEN;
    252         (void)deflate(strm, Z_NO_FLUSH);
    253         fwrite(out, 1, BUFLEN - strm->avail_out, gz->file);
    254     } while (strm->avail_out == 0);
    255     return len;
    256 }
    257 
    258 int gzread OF((gzFile, void *, unsigned));
    259 
    260 int gzread(gz, buf, len)
    261     gzFile gz;
    262     void *buf;
    263     unsigned len;
    264 {
    265     int ret;
    266     unsigned got;
    267     unsigned char in[1];
    268     z_stream *strm;
    269 
    270     if (gz == NULL || gz->write)
    271         return 0;
    272     if (gz->err)
    273         return 0;
    274     strm = &(gz->strm);
    275     strm->next_out = (void *)buf;
    276     strm->avail_out = len;
    277     do {
    278         got = fread(in, 1, 1, gz->file);
    279         if (got == 0)
    280             break;
    281         strm->next_in = in;
    282         strm->avail_in = 1;
    283         ret = inflate(strm, Z_NO_FLUSH);
    284         if (ret == Z_DATA_ERROR) {
    285             gz->err = Z_DATA_ERROR;
    286             gz->msg = strm->msg;
    287             return 0;
    288         }
    289         if (ret == Z_STREAM_END)
    290             inflateReset(strm);
    291     } while (strm->avail_out);
    292     return len - strm->avail_out;
    293 }
    294 
    295 int gzclose OF((gzFile));
    296 
    297 int gzclose(gz)
    298     gzFile gz;
    299 {
    300     z_stream *strm;
    301     unsigned char out[BUFLEN];
    302 
    303     if (gz == NULL)
    304         return Z_STREAM_ERROR;
    305     strm = &(gz->strm);
    306     if (gz->write) {
    307         strm->next_in = Z_NULL;
    308         strm->avail_in = 0;
    309         do {
    310             strm->next_out = out;
    311             strm->avail_out = BUFLEN;
    312             (void)deflate(strm, Z_FINISH);
    313             fwrite(out, 1, BUFLEN - strm->avail_out, gz->file);
    314         } while (strm->avail_out == 0);
    315         deflateEnd(strm);
    316     }
    317     else
    318         inflateEnd(strm);
    319     fclose(gz->file);
    320     free(gz);
    321     return Z_OK;
    322 }
    323 
    324 const char *gzerror OF((gzFile, int *));
    325 
    326 const char *gzerror(gz, err)
    327     gzFile gz;
    328     int *err;
    329 {
    330     *err = gz->err;
    331     return gz->msg;
    332 }
    333 
    334 #endif
    335 
    336 char *prog;
    337 
    338 void error            OF((const char *msg));
    339 void gz_compress      OF((FILE   *in, gzFile out));
    340 #ifdef USE_MMAP
    341 int  gz_compress_mmap OF((FILE   *in, gzFile out));
    342 #endif
    343 void gz_uncompress    OF((gzFile in, FILE   *out));
    344 void file_compress    OF((char  *file, char *mode));
    345 void file_uncompress  OF((char  *file));
    346 int  main             OF((int argc, char *argv[]));
    347 
    348 /* ===========================================================================
    349  * Display error message and exit
    350  */
    351 void error(msg)
    352     const char *msg;
    353 {
    354     fprintf(stderr, "%s: %s\n", prog, msg);
    355     exit(1);
    356 }
    357 
    358 /* ===========================================================================
    359  * Compress input to output then close both files.
    360  */
    361 
    362 void gz_compress(in, out)
    363     FILE   *in;
    364     gzFile out;
    365 {
    366     local char buf[BUFLEN];
    367     int len;
    368     int err;
    369 
    370 #ifdef USE_MMAP
    371     /* Try first compressing with mmap. If mmap fails (minigzip used in a
    372      * pipe), use the normal fread loop.
    373      */
    374     if (gz_compress_mmap(in, out) == Z_OK) return;
    375 #endif
    376     for (;;) {
    377         len = (int)fread(buf, 1, sizeof(buf), in);
    378         if (ferror(in)) {
    379             perror("fread");
    380             exit(1);
    381         }
    382         if (len == 0) break;
    383 
    384         if (gzwrite(out, buf, (unsigned)len) != len) error(gzerror(out, &err));
    385     }
    386     fclose(in);
    387     if (gzclose(out) != Z_OK) error("failed gzclose");
    388 }
    389 
    390 #ifdef USE_MMAP /* MMAP version, Miguel Albrecht <malbrech (at) eso.org> */
    391 
    392 /* Try compressing the input file at once using mmap. Return Z_OK if
    393  * if success, Z_ERRNO otherwise.
    394  */
    395 int gz_compress_mmap(in, out)
    396     FILE   *in;
    397     gzFile out;
    398 {
    399     int len;
    400     int err;
    401     int ifd = fileno(in);
    402     caddr_t buf;    /* mmap'ed buffer for the entire input file */
    403     off_t buf_len;  /* length of the input file */
    404     struct stat sb;
    405 
    406     /* Determine the size of the file, needed for mmap: */
    407     if (fstat(ifd, &sb) < 0) return Z_ERRNO;
    408     buf_len = sb.st_size;
    409     if (buf_len <= 0) return Z_ERRNO;
    410 
    411     /* Now do the actual mmap: */
    412     buf = mmap((caddr_t) 0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0);
    413     if (buf == (caddr_t)(-1)) return Z_ERRNO;
    414 
    415     /* Compress the whole file at once: */
    416     len = gzwrite(out, (char *)buf, (unsigned)buf_len);
    417 
    418     if (len != (int)buf_len) error(gzerror(out, &err));
    419 
    420     munmap(buf, buf_len);
    421     fclose(in);
    422     if (gzclose(out) != Z_OK) error("failed gzclose");
    423     return Z_OK;
    424 }
    425 #endif /* USE_MMAP */
    426 
    427 /* ===========================================================================
    428  * Uncompress input to output then close both files.
    429  */
    430 void gz_uncompress(in, out)
    431     gzFile in;
    432     FILE   *out;
    433 {
    434     local char buf[BUFLEN];
    435     int len;
    436     int err;
    437 
    438     for (;;) {
    439         len = gzread(in, buf, sizeof(buf));
    440         if (len < 0) error (gzerror(in, &err));
    441         if (len == 0) break;
    442 
    443         if ((int)fwrite(buf, 1, (unsigned)len, out) != len) {
    444             error("failed fwrite");
    445         }
    446     }
    447     if (fclose(out)) error("failed fclose");
    448 
    449     if (gzclose(in) != Z_OK) error("failed gzclose");
    450 }
    451 
    452 
    453 /* ===========================================================================
    454  * Compress the given file: create a corresponding .gz file and remove the
    455  * original.
    456  */
    457 void file_compress(file, mode)
    458     char  *file;
    459     char  *mode;
    460 {
    461     local char outfile[MAX_NAME_LEN];
    462     FILE  *in;
    463     gzFile out;
    464 
    465     if (strlen(file) + strlen(GZ_SUFFIX) >= sizeof(outfile)) {
    466         fprintf(stderr, "%s: filename too long\n", prog);
    467         exit(1);
    468     }
    469 
    470 #if !defined(NO_snprintf) && !defined(NO_vsnprintf)
    471     snprintf(outfile, sizeof(outfile), "%s%s", file, GZ_SUFFIX);
    472 #else
    473     strcpy(outfile, file);
    474     strcat(outfile, GZ_SUFFIX);
    475 #endif
    476 
    477     in = fopen(file, "rb");
    478     if (in == NULL) {
    479         perror(file);
    480         exit(1);
    481     }
    482     out = gzopen(outfile, mode);
    483     if (out == NULL) {
    484         fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile);
    485         exit(1);
    486     }
    487     gz_compress(in, out);
    488 
    489     unlink(file);
    490 }
    491 
    492 
    493 /* ===========================================================================
    494  * Uncompress the given file and remove the original.
    495  */
    496 void file_uncompress(file)
    497     char  *file;
    498 {
    499     local char buf[MAX_NAME_LEN];
    500     char *infile, *outfile;
    501     FILE  *out;
    502     gzFile in;
    503     size_t len = strlen(file);
    504 
    505     if (len + strlen(GZ_SUFFIX) >= sizeof(buf)) {
    506         fprintf(stderr, "%s: filename too long\n", prog);
    507         exit(1);
    508     }
    509 
    510 #if !defined(NO_snprintf) && !defined(NO_vsnprintf)
    511     snprintf(buf, sizeof(buf), "%s", file);
    512 #else
    513     strcpy(buf, file);
    514 #endif
    515 
    516     if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) {
    517         infile = file;
    518         outfile = buf;
    519         outfile[len-3] = '\0';
    520     } else {
    521         outfile = file;
    522         infile = buf;
    523 #if !defined(NO_snprintf) && !defined(NO_vsnprintf)
    524         snprintf(buf + len, sizeof(buf) - len, "%s", GZ_SUFFIX);
    525 #else
    526         strcat(infile, GZ_SUFFIX);
    527 #endif
    528     }
    529     in = gzopen(infile, "rb");
    530     if (in == NULL) {
    531         fprintf(stderr, "%s: can't gzopen %s\n", prog, infile);
    532         exit(1);
    533     }
    534     out = fopen(outfile, "wb");
    535     if (out == NULL) {
    536         perror(file);
    537         exit(1);
    538     }
    539 
    540     gz_uncompress(in, out);
    541 
    542     unlink(infile);
    543 }
    544 
    545 
    546 /* ===========================================================================
    547  * Usage:  minigzip [-c] [-d] [-f] [-h] [-r] [-1 to -9] [files...]
    548  *   -c : write to standard output
    549  *   -d : decompress
    550  *   -f : compress with Z_FILTERED
    551  *   -h : compress with Z_HUFFMAN_ONLY
    552  *   -r : compress with Z_RLE
    553  *   -1 to -9 : compression level
    554  */
    555 
    556 int main(argc, argv)
    557     int argc;
    558     char *argv[];
    559 {
    560     int copyout = 0;
    561     int uncompr = 0;
    562     gzFile file;
    563     char *bname, outmode[20];
    564 
    565 #if !defined(NO_snprintf) && !defined(NO_vsnprintf)
    566     snprintf(outmode, sizeof(outmode), "%s", "wb6 ");
    567 #else
    568     strcpy(outmode, "wb6 ");
    569 #endif
    570 
    571     prog = argv[0];
    572     bname = strrchr(argv[0], '/');
    573     if (bname)
    574       bname++;
    575     else
    576       bname = argv[0];
    577     argc--, argv++;
    578 
    579     if (!strcmp(bname, "gunzip"))
    580       uncompr = 1;
    581     else if (!strcmp(bname, "zcat"))
    582       copyout = uncompr = 1;
    583 
    584     while (argc > 0) {
    585       if (strcmp(*argv, "-c") == 0)
    586         copyout = 1;
    587       else if (strcmp(*argv, "-d") == 0)
    588         uncompr = 1;
    589       else if (strcmp(*argv, "-f") == 0)
    590         outmode[3] = 'f';
    591       else if (strcmp(*argv, "-h") == 0)
    592         outmode[3] = 'h';
    593       else if (strcmp(*argv, "-r") == 0)
    594         outmode[3] = 'R';
    595       else if ((*argv)[0] == '-' && (*argv)[1] >= '1' && (*argv)[1] <= '9' &&
    596                (*argv)[2] == 0)
    597         outmode[2] = (*argv)[1];
    598       else
    599         break;
    600       argc--, argv++;
    601     }
    602     if (outmode[3] == ' ')
    603         outmode[3] = 0;
    604     if (argc == 0) {
    605         SET_BINARY_MODE(stdin);
    606         SET_BINARY_MODE(stdout);
    607         if (uncompr) {
    608             file = gzdopen(fileno(stdin), "rb");
    609             if (file == NULL) error("can't gzdopen stdin");
    610             gz_uncompress(file, stdout);
    611         } else {
    612             file = gzdopen(fileno(stdout), outmode);
    613             if (file == NULL) error("can't gzdopen stdout");
    614             gz_compress(stdin, file);
    615         }
    616     } else {
    617         if (copyout) {
    618             SET_BINARY_MODE(stdout);
    619         }
    620         do {
    621             if (uncompr) {
    622                 if (copyout) {
    623                     file = gzopen(*argv, "rb");
    624                     if (file == NULL)
    625                         fprintf(stderr, "%s: can't gzopen %s\n", prog, *argv);
    626                     else
    627                         gz_uncompress(file, stdout);
    628                 } else {
    629                     file_uncompress(*argv);
    630                 }
    631             } else {
    632                 if (copyout) {
    633                     FILE * in = fopen(*argv, "rb");
    634 
    635                     if (in == NULL) {
    636                         perror(*argv);
    637                     } else {
    638                         file = gzdopen(fileno(stdout), outmode);
    639                         if (file == NULL) error("can't gzdopen stdout");
    640 
    641                         gz_compress(in, file);
    642                     }
    643 
    644                 } else {
    645                     file_compress(*argv, outmode);
    646                 }
    647             }
    648         } while (argv++, --argc);
    649     }
    650     return 0;
    651 }
    652