Home | History | Annotate | Download | only in zlib
      1 /* minigzip.c -- simulate gzip using the zlib compression library
      2  * Copyright (C) 1995-2006, 2010 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: minigzip.c,v 1.1.1.2 2002/03/11 21:53:26 tromey Exp $ */
     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 char *prog;
    142 
    143 void error            OF((const char *msg));
    144 void gz_compress      OF((FILE   *in, gzFile out));
    145 #ifdef USE_MMAP
    146 int  gz_compress_mmap OF((FILE   *in, gzFile out));
    147 #endif
    148 void gz_uncompress    OF((gzFile in, FILE   *out));
    149 void file_compress    OF((char  *file, char *mode));
    150 void file_uncompress  OF((char  *file));
    151 int  main             OF((int argc, char *argv[]));
    152 
    153 /* ===========================================================================
    154  * Display error message and exit
    155  */
    156 void error(msg)
    157     const char *msg;
    158 {
    159     fprintf(stderr, "%s: %s\n", prog, msg);
    160     exit(1);
    161 }
    162 
    163 /* ===========================================================================
    164  * Compress input to output then close both files.
    165  */
    166 
    167 void gz_compress(in, out)
    168     FILE   *in;
    169     gzFile out;
    170 {
    171     local char buf[BUFLEN];
    172     int len;
    173     int err;
    174 
    175 #ifdef USE_MMAP
    176     /* Try first compressing with mmap. If mmap fails (minigzip used in a
    177      * pipe), use the normal fread loop.
    178      */
    179     if (gz_compress_mmap(in, out) == Z_OK) return;
    180 #endif
    181     for (;;) {
    182         len = (int)fread(buf, 1, sizeof(buf), in);
    183         if (ferror(in)) {
    184             perror("fread");
    185             exit(1);
    186         }
    187         if (len == 0) break;
    188 
    189         if (gzwrite(out, buf, (unsigned)len) != len) error(gzerror(out, &err));
    190     }
    191     fclose(in);
    192     if (gzclose(out) != Z_OK) error("failed gzclose");
    193 }
    194 
    195 #ifdef USE_MMAP /* MMAP version, Miguel Albrecht <malbrech (at) eso.org> */
    196 
    197 /* Try compressing the input file at once using mmap. Return Z_OK if
    198  * if success, Z_ERRNO otherwise.
    199  */
    200 int gz_compress_mmap(in, out)
    201     FILE   *in;
    202     gzFile out;
    203 {
    204     int len;
    205     int err;
    206     int ifd = fileno(in);
    207     caddr_t buf;    /* mmap'ed buffer for the entire input file */
    208     off_t buf_len;  /* length of the input file */
    209     struct stat sb;
    210 
    211     /* Determine the size of the file, needed for mmap: */
    212     if (fstat(ifd, &sb) < 0) return Z_ERRNO;
    213     buf_len = sb.st_size;
    214     if (buf_len <= 0) return Z_ERRNO;
    215 
    216     /* Now do the actual mmap: */
    217     buf = mmap((caddr_t) 0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0);
    218     if (buf == (caddr_t)(-1)) return Z_ERRNO;
    219 
    220     /* Compress the whole file at once: */
    221     len = gzwrite(out, (char *)buf, (unsigned)buf_len);
    222 
    223     if (len != (int)buf_len) error(gzerror(out, &err));
    224 
    225     munmap(buf, buf_len);
    226     fclose(in);
    227     if (gzclose(out) != Z_OK) error("failed gzclose");
    228     return Z_OK;
    229 }
    230 #endif /* USE_MMAP */
    231 
    232 /* ===========================================================================
    233  * Uncompress input to output then close both files.
    234  */
    235 void gz_uncompress(in, out)
    236     gzFile in;
    237     FILE   *out;
    238 {
    239     local char buf[BUFLEN];
    240     int len;
    241     int err;
    242 
    243     for (;;) {
    244         len = gzread(in, buf, sizeof(buf));
    245         if (len < 0) error (gzerror(in, &err));
    246         if (len == 0) break;
    247 
    248         if ((int)fwrite(buf, 1, (unsigned)len, out) != len) {
    249             error("failed fwrite");
    250         }
    251     }
    252     if (fclose(out)) error("failed fclose");
    253 
    254     if (gzclose(in) != Z_OK) error("failed gzclose");
    255 }
    256 
    257 
    258 /* ===========================================================================
    259  * Compress the given file: create a corresponding .gz file and remove the
    260  * original.
    261  */
    262 void file_compress(file, mode)
    263     char  *file;
    264     char  *mode;
    265 {
    266     local char outfile[MAX_NAME_LEN];
    267     FILE  *in;
    268     gzFile out;
    269 
    270     if (strlen(file) + strlen(GZ_SUFFIX) >= sizeof(outfile)) {
    271         fprintf(stderr, "%s: filename too long\n", prog);
    272         exit(1);
    273     }
    274 
    275     strcpy(outfile, file);
    276     strcat(outfile, GZ_SUFFIX);
    277 
    278     in = fopen(file, "rb");
    279     if (in == NULL) {
    280         perror(file);
    281         exit(1);
    282     }
    283     out = gzopen(outfile, mode);
    284     if (out == NULL) {
    285         fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile);
    286         exit(1);
    287     }
    288     gz_compress(in, out);
    289 
    290     unlink(file);
    291 }
    292 
    293 
    294 /* ===========================================================================
    295  * Uncompress the given file and remove the original.
    296  */
    297 void file_uncompress(file)
    298     char  *file;
    299 {
    300     local char buf[MAX_NAME_LEN];
    301     char *infile, *outfile;
    302     FILE  *out;
    303     gzFile in;
    304     size_t len = strlen(file);
    305 
    306     if (len + strlen(GZ_SUFFIX) >= sizeof(buf)) {
    307         fprintf(stderr, "%s: filename too long\n", prog);
    308         exit(1);
    309     }
    310 
    311     strcpy(buf, file);
    312 
    313     if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) {
    314         infile = file;
    315         outfile = buf;
    316         outfile[len-3] = '\0';
    317     } else {
    318         outfile = file;
    319         infile = buf;
    320         strcat(infile, GZ_SUFFIX);
    321     }
    322     in = gzopen(infile, "rb");
    323     if (in == NULL) {
    324         fprintf(stderr, "%s: can't gzopen %s\n", prog, infile);
    325         exit(1);
    326     }
    327     out = fopen(outfile, "wb");
    328     if (out == NULL) {
    329         perror(file);
    330         exit(1);
    331     }
    332 
    333     gz_uncompress(in, out);
    334 
    335     unlink(infile);
    336 }
    337 
    338 
    339 /* ===========================================================================
    340  * Usage:  minigzip [-c] [-d] [-f] [-h] [-r] [-1 to -9] [files...]
    341  *   -c : write to standard output
    342  *   -d : decompress
    343  *   -f : compress with Z_FILTERED
    344  *   -h : compress with Z_HUFFMAN_ONLY
    345  *   -r : compress with Z_RLE
    346  *   -1 to -9 : compression level
    347  */
    348 
    349 int main(argc, argv)
    350     int argc;
    351     char *argv[];
    352 {
    353     int copyout = 0;
    354     int uncompr = 0;
    355     gzFile file;
    356     char *bname, outmode[20];
    357 
    358     strcpy(outmode, "wb6 ");
    359 
    360     prog = argv[0];
    361     bname = strrchr(argv[0], '/');
    362     if (bname)
    363       bname++;
    364     else
    365       bname = argv[0];
    366     argc--, argv++;
    367 
    368     if (!strcmp(bname, "gunzip"))
    369       uncompr = 1;
    370     else if (!strcmp(bname, "zcat"))
    371       copyout = uncompr = 1;
    372 
    373     while (argc > 0) {
    374       if (strcmp(*argv, "-c") == 0)
    375         copyout = 1;
    376       else if (strcmp(*argv, "-d") == 0)
    377         uncompr = 1;
    378       else if (strcmp(*argv, "-f") == 0)
    379         outmode[3] = 'f';
    380       else if (strcmp(*argv, "-h") == 0)
    381         outmode[3] = 'h';
    382       else if (strcmp(*argv, "-r") == 0)
    383         outmode[3] = 'R';
    384       else if ((*argv)[0] == '-' && (*argv)[1] >= '1' && (*argv)[1] <= '9' &&
    385                (*argv)[2] == 0)
    386         outmode[2] = (*argv)[1];
    387       else
    388         break;
    389       argc--, argv++;
    390     }
    391     if (outmode[3] == ' ')
    392         outmode[3] = 0;
    393     if (argc == 0) {
    394         SET_BINARY_MODE(stdin);
    395         SET_BINARY_MODE(stdout);
    396         if (uncompr) {
    397             file = gzdopen(fileno(stdin), "rb");
    398             if (file == NULL) error("can't gzdopen stdin");
    399             gz_uncompress(file, stdout);
    400         } else {
    401             file = gzdopen(fileno(stdout), outmode);
    402             if (file == NULL) error("can't gzdopen stdout");
    403             gz_compress(stdin, file);
    404         }
    405     } else {
    406         if (copyout) {
    407             SET_BINARY_MODE(stdout);
    408         }
    409         do {
    410             if (uncompr) {
    411                 if (copyout) {
    412                     file = gzopen(*argv, "rb");
    413                     if (file == NULL)
    414                         fprintf(stderr, "%s: can't gzopen %s\n", prog, *argv);
    415                     else
    416                         gz_uncompress(file, stdout);
    417                 } else {
    418                     file_uncompress(*argv);
    419                 }
    420             } else {
    421                 if (copyout) {
    422                     FILE * in = fopen(*argv, "rb");
    423 
    424                     if (in == NULL) {
    425                         perror(*argv);
    426                     } else {
    427                         file = gzdopen(fileno(stdout), outmode);
    428                         if (file == NULL) error("can't gzdopen stdout");
    429 
    430                         gz_compress(in, file);
    431                     }
    432 
    433                 } else {
    434                     file_compress(*argv, outmode);
    435                 }
    436             }
    437         } while (argv++, --argc);
    438     }
    439     return 0;
    440 }
    441