xref: /plan9/sys/src/cmd/gs/zlib/minigzip.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
17dd7cddfSDavid du Colombier /* minigzip.c -- simulate gzip using the zlib compression library
2*593dc095SDavid du Colombier  * Copyright (C) 1995-2002 Jean-loup Gailly.
37dd7cddfSDavid du Colombier  * For conditions of distribution and use, see copyright notice in zlib.h
47dd7cddfSDavid du Colombier  */
57dd7cddfSDavid du Colombier 
67dd7cddfSDavid du Colombier /*
77dd7cddfSDavid du Colombier  * minigzip is a minimal implementation of the gzip utility. This is
87dd7cddfSDavid du Colombier  * only an example of using zlib and isn't meant to replace the
97dd7cddfSDavid du Colombier  * full-featured gzip. No attempt is made to deal with file systems
107dd7cddfSDavid du Colombier  * limiting names to 14 or 8+3 characters, etc... Error checking is
117dd7cddfSDavid du Colombier  * very limited. So use minigzip only for testing; use gzip for the
127dd7cddfSDavid du Colombier  * real thing. On MSDOS, use only on file names without extension
137dd7cddfSDavid du Colombier  * or in pipe mode.
147dd7cddfSDavid du Colombier  */
157dd7cddfSDavid du Colombier 
16*593dc095SDavid du Colombier /* @(#) $Id: minigzip.c,v 1.1.1.1 2005/04/24 21:39:42 giles Exp $ */
177dd7cddfSDavid du Colombier 
187dd7cddfSDavid du Colombier #include <stdio.h>
197dd7cddfSDavid du Colombier #include "zlib.h"
207dd7cddfSDavid du Colombier 
217dd7cddfSDavid du Colombier #ifdef STDC
227dd7cddfSDavid du Colombier #  include <string.h>
237dd7cddfSDavid du Colombier #  include <stdlib.h>
247dd7cddfSDavid du Colombier #else
257dd7cddfSDavid du Colombier    extern void exit  OF((int));
267dd7cddfSDavid du Colombier #endif
277dd7cddfSDavid du Colombier 
28*593dc095SDavid du Colombier #ifdef USE_MMAP
29*593dc095SDavid du Colombier #  include <sys/types.h>
30*593dc095SDavid du Colombier #  include <sys/mman.h>
31*593dc095SDavid du Colombier #  include <sys/stat.h>
32*593dc095SDavid du Colombier #endif
337dd7cddfSDavid du Colombier 
34*593dc095SDavid du Colombier #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
357dd7cddfSDavid du Colombier #  include <fcntl.h>
367dd7cddfSDavid du Colombier #  include <io.h>
377dd7cddfSDavid du Colombier #  define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
387dd7cddfSDavid du Colombier #else
397dd7cddfSDavid du Colombier #  define SET_BINARY_MODE(file)
407dd7cddfSDavid du Colombier #endif
417dd7cddfSDavid du Colombier 
427dd7cddfSDavid du Colombier #ifdef VMS
437dd7cddfSDavid du Colombier #  define unlink delete
447dd7cddfSDavid du Colombier #  define GZ_SUFFIX "-gz"
457dd7cddfSDavid du Colombier #endif
467dd7cddfSDavid du Colombier #ifdef RISCOS
477dd7cddfSDavid du Colombier #  define unlink remove
487dd7cddfSDavid du Colombier #  define GZ_SUFFIX "-gz"
497dd7cddfSDavid du Colombier #  define fileno(file) file->__file
507dd7cddfSDavid du Colombier #endif
51*593dc095SDavid du Colombier #if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
52*593dc095SDavid du Colombier #  include <unix.h> /* for fileno */
53*593dc095SDavid du Colombier #endif
54*593dc095SDavid du Colombier 
55*593dc095SDavid du Colombier #ifndef WIN32 /* unlink already in stdio.h for WIN32 */
56*593dc095SDavid du Colombier   extern int unlink OF((const char *));
57*593dc095SDavid du Colombier #endif
587dd7cddfSDavid du Colombier 
597dd7cddfSDavid du Colombier #ifndef GZ_SUFFIX
607dd7cddfSDavid du Colombier #  define GZ_SUFFIX ".gz"
617dd7cddfSDavid du Colombier #endif
62*593dc095SDavid du Colombier #define SUFFIX_LEN (sizeof(GZ_SUFFIX)-1)
637dd7cddfSDavid du Colombier 
64*593dc095SDavid du Colombier #define BUFLEN      16384
657dd7cddfSDavid du Colombier #define MAX_NAME_LEN 1024
667dd7cddfSDavid du Colombier 
67*593dc095SDavid du Colombier #ifdef MAXSEG_64K
687dd7cddfSDavid du Colombier #  define local static
69*593dc095SDavid du Colombier    /* Needed for systems with limitation on stack size. */
70*593dc095SDavid du Colombier #else
717dd7cddfSDavid du Colombier #  define local
72*593dc095SDavid du Colombier #endif
737dd7cddfSDavid du Colombier 
747dd7cddfSDavid du Colombier char *prog;
757dd7cddfSDavid du Colombier 
767dd7cddfSDavid du Colombier void error            OF((const char *msg));
777dd7cddfSDavid du Colombier void gz_compress      OF((FILE   *in, gzFile out));
78*593dc095SDavid du Colombier #ifdef USE_MMAP
79*593dc095SDavid du Colombier int  gz_compress_mmap OF((FILE   *in, gzFile out));
80*593dc095SDavid du Colombier #endif
817dd7cddfSDavid du Colombier void gz_uncompress    OF((gzFile in, FILE   *out));
82*593dc095SDavid du Colombier void file_compress    OF((char  *file, char *mode));
837dd7cddfSDavid du Colombier void file_uncompress  OF((char  *file));
847dd7cddfSDavid du Colombier int  main             OF((int argc, char *argv[]));
857dd7cddfSDavid du Colombier 
867dd7cddfSDavid du Colombier /* ===========================================================================
877dd7cddfSDavid du Colombier  * Display error message and exit
887dd7cddfSDavid du Colombier  */
error(msg)897dd7cddfSDavid du Colombier void error(msg)
907dd7cddfSDavid du Colombier     const char *msg;
917dd7cddfSDavid du Colombier {
927dd7cddfSDavid du Colombier     fprintf(stderr, "%s: %s\n", prog, msg);
937dd7cddfSDavid du Colombier     exit(1);
947dd7cddfSDavid du Colombier }
957dd7cddfSDavid du Colombier 
967dd7cddfSDavid du Colombier /* ===========================================================================
977dd7cddfSDavid du Colombier  * Compress input to output then close both files.
987dd7cddfSDavid du Colombier  */
99*593dc095SDavid du Colombier 
gz_compress(in,out)1007dd7cddfSDavid du Colombier void gz_compress(in, out)
1017dd7cddfSDavid du Colombier     FILE   *in;
1027dd7cddfSDavid du Colombier     gzFile out;
1037dd7cddfSDavid du Colombier {
1047dd7cddfSDavid du Colombier     local char buf[BUFLEN];
1057dd7cddfSDavid du Colombier     int len;
1067dd7cddfSDavid du Colombier     int err;
1077dd7cddfSDavid du Colombier 
108*593dc095SDavid du Colombier #ifdef USE_MMAP
109*593dc095SDavid du Colombier     /* Try first compressing with mmap. If mmap fails (minigzip used in a
110*593dc095SDavid du Colombier      * pipe), use the normal fread loop.
111*593dc095SDavid du Colombier      */
112*593dc095SDavid du Colombier     if (gz_compress_mmap(in, out) == Z_OK) return;
113*593dc095SDavid du Colombier #endif
1147dd7cddfSDavid du Colombier     for (;;) {
115*593dc095SDavid du Colombier         len = (int)fread(buf, 1, sizeof(buf), in);
1167dd7cddfSDavid du Colombier         if (ferror(in)) {
1177dd7cddfSDavid du Colombier             perror("fread");
1187dd7cddfSDavid du Colombier             exit(1);
1197dd7cddfSDavid du Colombier         }
1207dd7cddfSDavid du Colombier         if (len == 0) break;
1217dd7cddfSDavid du Colombier 
1227dd7cddfSDavid du Colombier         if (gzwrite(out, buf, (unsigned)len) != len) error(gzerror(out, &err));
1237dd7cddfSDavid du Colombier     }
1247dd7cddfSDavid du Colombier     fclose(in);
1257dd7cddfSDavid du Colombier     if (gzclose(out) != Z_OK) error("failed gzclose");
1267dd7cddfSDavid du Colombier }
1277dd7cddfSDavid du Colombier 
128*593dc095SDavid du Colombier #ifdef USE_MMAP /* MMAP version, Miguel Albrecht <malbrech@eso.org> */
129*593dc095SDavid du Colombier 
130*593dc095SDavid du Colombier /* Try compressing the input file at once using mmap. Return Z_OK if
131*593dc095SDavid du Colombier  * if success, Z_ERRNO otherwise.
132*593dc095SDavid du Colombier  */
gz_compress_mmap(in,out)133*593dc095SDavid du Colombier int gz_compress_mmap(in, out)
134*593dc095SDavid du Colombier     FILE   *in;
135*593dc095SDavid du Colombier     gzFile out;
136*593dc095SDavid du Colombier {
137*593dc095SDavid du Colombier     int len;
138*593dc095SDavid du Colombier     int err;
139*593dc095SDavid du Colombier     int ifd = fileno(in);
140*593dc095SDavid du Colombier     caddr_t buf;    /* mmap'ed buffer for the entire input file */
141*593dc095SDavid du Colombier     off_t buf_len;  /* length of the input file */
142*593dc095SDavid du Colombier     struct stat sb;
143*593dc095SDavid du Colombier 
144*593dc095SDavid du Colombier     /* Determine the size of the file, needed for mmap: */
145*593dc095SDavid du Colombier     if (fstat(ifd, &sb) < 0) return Z_ERRNO;
146*593dc095SDavid du Colombier     buf_len = sb.st_size;
147*593dc095SDavid du Colombier     if (buf_len <= 0) return Z_ERRNO;
148*593dc095SDavid du Colombier 
149*593dc095SDavid du Colombier     /* Now do the actual mmap: */
150*593dc095SDavid du Colombier     buf = mmap((caddr_t) 0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0);
151*593dc095SDavid du Colombier     if (buf == (caddr_t)(-1)) return Z_ERRNO;
152*593dc095SDavid du Colombier 
153*593dc095SDavid du Colombier     /* Compress the whole file at once: */
154*593dc095SDavid du Colombier     len = gzwrite(out, (char *)buf, (unsigned)buf_len);
155*593dc095SDavid du Colombier 
156*593dc095SDavid du Colombier     if (len != (int)buf_len) error(gzerror(out, &err));
157*593dc095SDavid du Colombier 
158*593dc095SDavid du Colombier     munmap(buf, buf_len);
159*593dc095SDavid du Colombier     fclose(in);
160*593dc095SDavid du Colombier     if (gzclose(out) != Z_OK) error("failed gzclose");
161*593dc095SDavid du Colombier     return Z_OK;
162*593dc095SDavid du Colombier }
163*593dc095SDavid du Colombier #endif /* USE_MMAP */
164*593dc095SDavid du Colombier 
1657dd7cddfSDavid du Colombier /* ===========================================================================
1667dd7cddfSDavid du Colombier  * Uncompress input to output then close both files.
1677dd7cddfSDavid du Colombier  */
gz_uncompress(in,out)1687dd7cddfSDavid du Colombier void gz_uncompress(in, out)
1697dd7cddfSDavid du Colombier     gzFile in;
1707dd7cddfSDavid du Colombier     FILE   *out;
1717dd7cddfSDavid du Colombier {
1727dd7cddfSDavid du Colombier     local char buf[BUFLEN];
1737dd7cddfSDavid du Colombier     int len;
1747dd7cddfSDavid du Colombier     int err;
1757dd7cddfSDavid du Colombier 
1767dd7cddfSDavid du Colombier     for (;;) {
1777dd7cddfSDavid du Colombier         len = gzread(in, buf, sizeof(buf));
1787dd7cddfSDavid du Colombier         if (len < 0) error (gzerror(in, &err));
1797dd7cddfSDavid du Colombier         if (len == 0) break;
1807dd7cddfSDavid du Colombier 
1817dd7cddfSDavid du Colombier         if ((int)fwrite(buf, 1, (unsigned)len, out) != len) {
1827dd7cddfSDavid du Colombier             error("failed fwrite");
1837dd7cddfSDavid du Colombier         }
1847dd7cddfSDavid du Colombier     }
1857dd7cddfSDavid du Colombier     if (fclose(out)) error("failed fclose");
1867dd7cddfSDavid du Colombier 
1877dd7cddfSDavid du Colombier     if (gzclose(in) != Z_OK) error("failed gzclose");
1887dd7cddfSDavid du Colombier }
1897dd7cddfSDavid du Colombier 
1907dd7cddfSDavid du Colombier 
1917dd7cddfSDavid du Colombier /* ===========================================================================
1927dd7cddfSDavid du Colombier  * Compress the given file: create a corresponding .gz file and remove the
1937dd7cddfSDavid du Colombier  * original.
1947dd7cddfSDavid du Colombier  */
file_compress(file,mode)195*593dc095SDavid du Colombier void file_compress(file, mode)
1967dd7cddfSDavid du Colombier     char  *file;
197*593dc095SDavid du Colombier     char  *mode;
1987dd7cddfSDavid du Colombier {
1997dd7cddfSDavid du Colombier     local char outfile[MAX_NAME_LEN];
2007dd7cddfSDavid du Colombier     FILE  *in;
2017dd7cddfSDavid du Colombier     gzFile out;
2027dd7cddfSDavid du Colombier 
2037dd7cddfSDavid du Colombier     strcpy(outfile, file);
2047dd7cddfSDavid du Colombier     strcat(outfile, GZ_SUFFIX);
2057dd7cddfSDavid du Colombier 
2067dd7cddfSDavid du Colombier     in = fopen(file, "rb");
2077dd7cddfSDavid du Colombier     if (in == NULL) {
2087dd7cddfSDavid du Colombier         perror(file);
2097dd7cddfSDavid du Colombier         exit(1);
2107dd7cddfSDavid du Colombier     }
211*593dc095SDavid du Colombier     out = gzopen(outfile, mode);
2127dd7cddfSDavid du Colombier     if (out == NULL) {
2137dd7cddfSDavid du Colombier         fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile);
2147dd7cddfSDavid du Colombier         exit(1);
2157dd7cddfSDavid du Colombier     }
2167dd7cddfSDavid du Colombier     gz_compress(in, out);
2177dd7cddfSDavid du Colombier 
2187dd7cddfSDavid du Colombier     unlink(file);
2197dd7cddfSDavid du Colombier }
2207dd7cddfSDavid du Colombier 
2217dd7cddfSDavid du Colombier 
2227dd7cddfSDavid du Colombier /* ===========================================================================
2237dd7cddfSDavid du Colombier  * Uncompress the given file and remove the original.
2247dd7cddfSDavid du Colombier  */
file_uncompress(file)2257dd7cddfSDavid du Colombier void file_uncompress(file)
2267dd7cddfSDavid du Colombier     char  *file;
2277dd7cddfSDavid du Colombier {
2287dd7cddfSDavid du Colombier     local char buf[MAX_NAME_LEN];
2297dd7cddfSDavid du Colombier     char *infile, *outfile;
2307dd7cddfSDavid du Colombier     FILE  *out;
2317dd7cddfSDavid du Colombier     gzFile in;
232*593dc095SDavid du Colombier     uInt len = (uInt)strlen(file);
2337dd7cddfSDavid du Colombier 
2347dd7cddfSDavid du Colombier     strcpy(buf, file);
2357dd7cddfSDavid du Colombier 
2367dd7cddfSDavid du Colombier     if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) {
2377dd7cddfSDavid du Colombier         infile = file;
2387dd7cddfSDavid du Colombier         outfile = buf;
2397dd7cddfSDavid du Colombier         outfile[len-3] = '\0';
2407dd7cddfSDavid du Colombier     } else {
2417dd7cddfSDavid du Colombier         outfile = file;
2427dd7cddfSDavid du Colombier         infile = buf;
2437dd7cddfSDavid du Colombier         strcat(infile, GZ_SUFFIX);
2447dd7cddfSDavid du Colombier     }
2457dd7cddfSDavid du Colombier     in = gzopen(infile, "rb");
2467dd7cddfSDavid du Colombier     if (in == NULL) {
2477dd7cddfSDavid du Colombier         fprintf(stderr, "%s: can't gzopen %s\n", prog, infile);
2487dd7cddfSDavid du Colombier         exit(1);
2497dd7cddfSDavid du Colombier     }
2507dd7cddfSDavid du Colombier     out = fopen(outfile, "wb");
2517dd7cddfSDavid du Colombier     if (out == NULL) {
2527dd7cddfSDavid du Colombier         perror(file);
2537dd7cddfSDavid du Colombier         exit(1);
2547dd7cddfSDavid du Colombier     }
2557dd7cddfSDavid du Colombier 
2567dd7cddfSDavid du Colombier     gz_uncompress(in, out);
2577dd7cddfSDavid du Colombier 
2587dd7cddfSDavid du Colombier     unlink(infile);
2597dd7cddfSDavid du Colombier }
2607dd7cddfSDavid du Colombier 
2617dd7cddfSDavid du Colombier 
2627dd7cddfSDavid du Colombier /* ===========================================================================
263*593dc095SDavid du Colombier  * Usage:  minigzip [-d] [-f] [-h] [-r] [-1 to -9] [files...]
264*593dc095SDavid du Colombier  *   -d : decompress
265*593dc095SDavid du Colombier  *   -f : compress with Z_FILTERED
266*593dc095SDavid du Colombier  *   -h : compress with Z_HUFFMAN_ONLY
267*593dc095SDavid du Colombier  *   -r : compress with Z_RLE
268*593dc095SDavid du Colombier  *   -1 to -9 : compression level
2697dd7cddfSDavid du Colombier  */
2707dd7cddfSDavid du Colombier 
main(argc,argv)2717dd7cddfSDavid du Colombier int main(argc, argv)
2727dd7cddfSDavid du Colombier     int argc;
2737dd7cddfSDavid du Colombier     char *argv[];
2747dd7cddfSDavid du Colombier {
2757dd7cddfSDavid du Colombier     int uncompr = 0;
2767dd7cddfSDavid du Colombier     gzFile file;
277*593dc095SDavid du Colombier     char outmode[20];
278*593dc095SDavid du Colombier 
279*593dc095SDavid du Colombier     strcpy(outmode, "wb6 ");
2807dd7cddfSDavid du Colombier 
2817dd7cddfSDavid du Colombier     prog = argv[0];
2827dd7cddfSDavid du Colombier     argc--, argv++;
2837dd7cddfSDavid du Colombier 
284*593dc095SDavid du Colombier     while (argc > 0) {
285*593dc095SDavid du Colombier       if (strcmp(*argv, "-d") == 0)
286*593dc095SDavid du Colombier         uncompr = 1;
287*593dc095SDavid du Colombier       else if (strcmp(*argv, "-f") == 0)
288*593dc095SDavid du Colombier         outmode[3] = 'f';
289*593dc095SDavid du Colombier       else if (strcmp(*argv, "-h") == 0)
290*593dc095SDavid du Colombier         outmode[3] = 'h';
291*593dc095SDavid du Colombier       else if (strcmp(*argv, "-r") == 0)
292*593dc095SDavid du Colombier         outmode[3] = 'R';
293*593dc095SDavid du Colombier       else if ((*argv)[0] == '-' && (*argv)[1] >= '1' && (*argv)[1] <= '9' &&
294*593dc095SDavid du Colombier                (*argv)[2] == 0)
295*593dc095SDavid du Colombier         outmode[2] = (*argv)[1];
296*593dc095SDavid du Colombier       else
297*593dc095SDavid du Colombier         break;
2987dd7cddfSDavid du Colombier       argc--, argv++;
2997dd7cddfSDavid du Colombier     }
3007dd7cddfSDavid du Colombier     if (argc == 0) {
3017dd7cddfSDavid du Colombier         SET_BINARY_MODE(stdin);
3027dd7cddfSDavid du Colombier         SET_BINARY_MODE(stdout);
3037dd7cddfSDavid du Colombier         if (uncompr) {
3047dd7cddfSDavid du Colombier             file = gzdopen(fileno(stdin), "rb");
3057dd7cddfSDavid du Colombier             if (file == NULL) error("can't gzdopen stdin");
3067dd7cddfSDavid du Colombier             gz_uncompress(file, stdout);
3077dd7cddfSDavid du Colombier         } else {
308*593dc095SDavid du Colombier             file = gzdopen(fileno(stdout), outmode);
3097dd7cddfSDavid du Colombier             if (file == NULL) error("can't gzdopen stdout");
3107dd7cddfSDavid du Colombier             gz_compress(stdin, file);
3117dd7cddfSDavid du Colombier         }
3127dd7cddfSDavid du Colombier     } else {
3137dd7cddfSDavid du Colombier         do {
3147dd7cddfSDavid du Colombier             if (uncompr) {
3157dd7cddfSDavid du Colombier                 file_uncompress(*argv);
3167dd7cddfSDavid du Colombier             } else {
317*593dc095SDavid du Colombier                 file_compress(*argv, outmode);
3187dd7cddfSDavid du Colombier             }
3197dd7cddfSDavid du Colombier         } while (argv++, --argc);
3207dd7cddfSDavid du Colombier     }
321*593dc095SDavid du Colombier     return 0;
3227dd7cddfSDavid du Colombier }
323