]> git.lizzy.rs Git - zlib.git/blob - minigzip.c
zlib 1.2.3.5
[zlib.git] / minigzip.c
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$ */
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 #  define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
36 #else
37 #  define SET_BINARY_MODE(file)
38 #endif
39
40 #ifdef VMS
41 #  define unlink delete
42 #  define GZ_SUFFIX "-gz"
43 #endif
44 #ifdef RISCOS
45 #  define unlink remove
46 #  define GZ_SUFFIX "-gz"
47 #  define fileno(file) file->__file
48 #endif
49 #if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
50 #  include <unix.h> /* for fileno */
51 #endif
52
53 #ifndef WIN32 /* unlink already in stdio.h for WIN32 */
54   extern int unlink OF((const char *));
55 #endif
56
57 #if defined(UNDER_CE) && defined(NO_ERRNO_H)
58 #  include <windows.h>
59 #  define perror(s) pwinerror(s)
60
61 /* Map the Windows error number in ERROR to a locale-dependent error
62    message string and return a pointer to it.  Typically, the values
63    for ERROR come from GetLastError.
64
65    The string pointed to shall not be modified by the application,
66    but may be overwritten by a subsequent call to strwinerror
67
68    The strwinerror function does not change the current setting
69    of GetLastError.  */
70
71 static char *strwinerror (error)
72      DWORD error;
73 {
74     static char buf[1024];
75
76     wchar_t *msgbuf;
77     DWORD lasterr = GetLastError();
78     DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
79         | FORMAT_MESSAGE_ALLOCATE_BUFFER,
80         NULL,
81         error,
82         0, /* Default language */
83         (LPVOID)&msgbuf,
84         0,
85         NULL);
86     if (chars != 0) {
87         /* If there is an \r\n appended, zap it.  */
88         if (chars >= 2
89             && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') {
90             chars -= 2;
91             msgbuf[chars] = 0;
92         }
93
94         if (chars > sizeof (buf) - 1) {
95             chars = sizeof (buf) - 1;
96             msgbuf[chars] = 0;
97         }
98
99         wcstombs(buf, msgbuf, chars + 1);
100         LocalFree(msgbuf);
101     }
102     else {
103         sprintf(buf, "unknown win32 error (%ld)", error);
104     }
105
106     SetLastError(lasterr);
107     return buf;
108 }
109
110 static void pwinerror (s)
111     const char *s;
112 {
113     if (s && *s)
114         fprintf(stderr, "%s: %s\n", s, strwinerror(GetLastError ()));
115     else
116         fprintf(stderr, "%s\n", strwinerror(GetLastError ()));
117 }
118
119 #endif /* UNDER_CE && NO_ERRNO_H */
120
121 #ifndef GZ_SUFFIX
122 #  define GZ_SUFFIX ".gz"
123 #endif
124 #define SUFFIX_LEN (sizeof(GZ_SUFFIX)-1)
125
126 #define BUFLEN      16384
127 #define MAX_NAME_LEN 1024
128
129 #ifdef MAXSEG_64K
130 #  define local static
131    /* Needed for systems with limitation on stack size. */
132 #else
133 #  define local
134 #endif
135
136 char *prog;
137
138 void error            OF((const char *msg));
139 void gz_compress      OF((FILE   *in, gzFile out));
140 #ifdef USE_MMAP
141 int  gz_compress_mmap OF((FILE   *in, gzFile out));
142 #endif
143 void gz_uncompress    OF((gzFile in, FILE   *out));
144 void file_compress    OF((char  *file, char *mode));
145 void file_uncompress  OF((char  *file));
146 int  main             OF((int argc, char *argv[]));
147
148 /* ===========================================================================
149  * Display error message and exit
150  */
151 void error(msg)
152     const char *msg;
153 {
154     fprintf(stderr, "%s: %s\n", prog, msg);
155     exit(1);
156 }
157
158 /* ===========================================================================
159  * Compress input to output then close both files.
160  */
161
162 void gz_compress(in, out)
163     FILE   *in;
164     gzFile out;
165 {
166     local char buf[BUFLEN];
167     int len;
168     int err;
169
170 #ifdef USE_MMAP
171     /* Try first compressing with mmap. If mmap fails (minigzip used in a
172      * pipe), use the normal fread loop.
173      */
174     if (gz_compress_mmap(in, out) == Z_OK) return;
175 #endif
176     for (;;) {
177         len = (int)fread(buf, 1, sizeof(buf), in);
178         if (ferror(in)) {
179             perror("fread");
180             exit(1);
181         }
182         if (len == 0) break;
183
184         if (gzwrite(out, buf, (unsigned)len) != len) error(gzerror(out, &err));
185     }
186     fclose(in);
187     if (gzclose(out) != Z_OK) error("failed gzclose");
188 }
189
190 #ifdef USE_MMAP /* MMAP version, Miguel Albrecht <malbrech@eso.org> */
191
192 /* Try compressing the input file at once using mmap. Return Z_OK if
193  * if success, Z_ERRNO otherwise.
194  */
195 int gz_compress_mmap(in, out)
196     FILE   *in;
197     gzFile out;
198 {
199     int len;
200     int err;
201     int ifd = fileno(in);
202     caddr_t buf;    /* mmap'ed buffer for the entire input file */
203     off_t buf_len;  /* length of the input file */
204     struct stat sb;
205
206     /* Determine the size of the file, needed for mmap: */
207     if (fstat(ifd, &sb) < 0) return Z_ERRNO;
208     buf_len = sb.st_size;
209     if (buf_len <= 0) return Z_ERRNO;
210
211     /* Now do the actual mmap: */
212     buf = mmap((caddr_t) 0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0);
213     if (buf == (caddr_t)(-1)) return Z_ERRNO;
214
215     /* Compress the whole file at once: */
216     len = gzwrite(out, (char *)buf, (unsigned)buf_len);
217
218     if (len != (int)buf_len) error(gzerror(out, &err));
219
220     munmap(buf, buf_len);
221     fclose(in);
222     if (gzclose(out) != Z_OK) error("failed gzclose");
223     return Z_OK;
224 }
225 #endif /* USE_MMAP */
226
227 /* ===========================================================================
228  * Uncompress input to output then close both files.
229  */
230 void gz_uncompress(in, out)
231     gzFile in;
232     FILE   *out;
233 {
234     local char buf[BUFLEN];
235     int len;
236     int err;
237
238     for (;;) {
239         len = gzread(in, buf, sizeof(buf));
240         if (len < 0) error (gzerror(in, &err));
241         if (len == 0) break;
242
243         if ((int)fwrite(buf, 1, (unsigned)len, out) != len) {
244             error("failed fwrite");
245         }
246     }
247     if (fclose(out)) error("failed fclose");
248
249     if (gzclose(in) != Z_OK) error("failed gzclose");
250 }
251
252
253 /* ===========================================================================
254  * Compress the given file: create a corresponding .gz file and remove the
255  * original.
256  */
257 void file_compress(file, mode)
258     char  *file;
259     char  *mode;
260 {
261     local char outfile[MAX_NAME_LEN];
262     FILE  *in;
263     gzFile out;
264
265     strcpy(outfile, file);
266     strcat(outfile, GZ_SUFFIX);
267
268     in = fopen(file, "rb");
269     if (in == NULL) {
270         perror(file);
271         exit(1);
272     }
273     out = gzopen(outfile, mode);
274     if (out == NULL) {
275         fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile);
276         exit(1);
277     }
278     gz_compress(in, out);
279
280     unlink(file);
281 }
282
283
284 /* ===========================================================================
285  * Uncompress the given file and remove the original.
286  */
287 void file_uncompress(file)
288     char  *file;
289 {
290     local char buf[MAX_NAME_LEN];
291     char *infile, *outfile;
292     FILE  *out;
293     gzFile in;
294     uInt len = (uInt)strlen(file);
295
296     strcpy(buf, file);
297
298     if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) {
299         infile = file;
300         outfile = buf;
301         outfile[len-3] = '\0';
302     } else {
303         outfile = file;
304         infile = buf;
305         strcat(infile, GZ_SUFFIX);
306     }
307     in = gzopen(infile, "rb");
308     if (in == NULL) {
309         fprintf(stderr, "%s: can't gzopen %s\n", prog, infile);
310         exit(1);
311     }
312     out = fopen(outfile, "wb");
313     if (out == NULL) {
314         perror(file);
315         exit(1);
316     }
317
318     gz_uncompress(in, out);
319
320     unlink(infile);
321 }
322
323
324 /* ===========================================================================
325  * Usage:  minigzip [-d] [-f] [-h] [-r] [-1 to -9] [files...]
326  *   -d : decompress
327  *   -f : compress with Z_FILTERED
328  *   -h : compress with Z_HUFFMAN_ONLY
329  *   -r : compress with Z_RLE
330  *   -1 to -9 : compression level
331  */
332
333 int main(argc, argv)
334     int argc;
335     char *argv[];
336 {
337     int uncompr = 0;
338     gzFile file;
339     char outmode[20];
340
341     strcpy(outmode, "wb6 ");
342
343     prog = argv[0];
344     argc--, argv++;
345
346     while (argc > 0) {
347       if (strcmp(*argv, "-d") == 0)
348         uncompr = 1;
349       else if (strcmp(*argv, "-f") == 0)
350         outmode[3] = 'f';
351       else if (strcmp(*argv, "-h") == 0)
352         outmode[3] = 'h';
353       else if (strcmp(*argv, "-r") == 0)
354         outmode[3] = 'R';
355       else if ((*argv)[0] == '-' && (*argv)[1] >= '1' && (*argv)[1] <= '9' &&
356                (*argv)[2] == 0)
357         outmode[2] = (*argv)[1];
358       else
359         break;
360       argc--, argv++;
361     }
362     if (outmode[3] == ' ')
363         outmode[3] = 0;
364     if (argc == 0) {
365         SET_BINARY_MODE(stdin);
366         SET_BINARY_MODE(stdout);
367         if (uncompr) {
368             file = gzdopen(fileno(stdin), "rb");
369             if (file == NULL) error("can't gzdopen stdin");
370             gz_uncompress(file, stdout);
371         } else {
372             file = gzdopen(fileno(stdout), outmode);
373             if (file == NULL) error("can't gzdopen stdout");
374             gz_compress(stdin, file);
375         }
376     } else {
377         do {
378             if (uncompr) {
379                 file_uncompress(*argv);
380             } else {
381                 file_compress(*argv, outmode);
382             }
383         } while (argv++, --argc);
384     }
385     return 0;
386 }