1 /* minigzip.c -- simulate gzip using the zlib compression library
2 * Copyright (C) 1995-2006, 2010, 2011, 2016 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.
13 */
14
15 #include "zbuild.h"
16 #ifdef ZLIB_COMPAT
17 # include "zlib.h"
18 #else
19 # include "zlib-ng.h"
20 #endif
21 #include <stdio.h>
22
23 #include <string.h>
24 #include <stdlib.h>
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(_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 #if defined(_MSC_VER) && _MSC_VER < 1900
41 # define snprintf _snprintf
42 #endif
43
44 #if !defined(Z_HAVE_UNISTD_H) && !defined(_LARGEFILE64_SOURCE)
45 #ifndef _WIN32 /* unlink already in stdio.h for Win32 */
46 extern int unlink (const char *);
47 #endif
48 #endif
49
50 #ifndef GZ_SUFFIX
51 # define GZ_SUFFIX ".gz"
52 #endif
53 #define SUFFIX_LEN (sizeof(GZ_SUFFIX)-1)
54
55 #ifndef BUFLEN
56 # define BUFLEN 16384 /* read buffer size */
57 #endif
58 #define BUFLENW (BUFLEN * 3) /* write buffer size */
59 #define MAX_NAME_LEN 1024
60
61 static char *prog;
62
63 void error (const char *msg);
64 void gz_compress (FILE *in, gzFile out);
65 #ifdef USE_MMAP
66 int gz_compress_mmap (FILE *in, gzFile out);
67 #endif
68 void gz_uncompress (gzFile in, FILE *out);
69 void file_compress (char *file, char *mode, int keep);
70 void file_uncompress (char *file, int keep);
71 int main (int argc, char *argv[]);
72
73 /* ===========================================================================
74 * Display error message and exit
75 */
error(const char * msg)76 void error(const char *msg) {
77 fprintf(stderr, "%s: %s\n", prog, msg);
78 exit(1);
79 }
80
81 /* ===========================================================================
82 * Compress input to output then close both files.
83 */
84
gz_compress(FILE * in,gzFile out)85 void gz_compress(FILE *in, gzFile out) {
86 char *buf;
87 int len;
88 int err;
89
90 #ifdef USE_MMAP
91 /* Try first compressing with mmap. If mmap fails (minigzip used in a
92 * pipe), use the normal fread loop.
93 */
94 if (gz_compress_mmap(in, out) == Z_OK) return;
95 #endif
96 buf = (char *)calloc(BUFLEN, 1);
97 if (buf == NULL) {
98 perror("out of memory");
99 exit(1);
100 }
101
102 for (;;) {
103 len = (int)fread(buf, 1, BUFLEN, in);
104 if (ferror(in)) {
105 free(buf);
106 perror("fread");
107 exit(1);
108 }
109 if (len == 0) break;
110
111 if (PREFIX(gzwrite)(out, buf, (unsigned)len) != len) error(PREFIX(gzerror)(out, &err));
112 }
113 free(buf);
114 fclose(in);
115 if (PREFIX(gzclose)(out) != Z_OK) error("failed gzclose");
116 }
117
118 #ifdef USE_MMAP /* MMAP version, Miguel Albrecht <[email protected]> */
119
120 /* Try compressing the input file at once using mmap. Return Z_OK if
121 * if success, Z_ERRNO otherwise.
122 */
gz_compress_mmap(FILE * in,gzFile out)123 int gz_compress_mmap(FILE *in, gzFile out) {
124 int len;
125 int err;
126 int ifd = fileno(in);
127 char *buf; /* mmap'ed buffer for the entire input file */
128 off_t buf_len; /* length of the input file */
129 struct stat sb;
130
131 /* Determine the size of the file, needed for mmap: */
132 if (fstat(ifd, &sb) < 0) return Z_ERRNO;
133 buf_len = sb.st_size;
134 if (buf_len <= 0) return Z_ERRNO;
135
136 /* Now do the actual mmap: */
137 buf = mmap((void *)0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0);
138 if (buf == (char *)(-1)) return Z_ERRNO;
139
140 /* Compress the whole file at once: */
141 len = PREFIX(gzwrite)(out, buf, (unsigned)buf_len);
142
143 if (len != (int)buf_len) error(PREFIX(gzerror)(out, &err));
144
145 munmap(buf, buf_len);
146 fclose(in);
147 if (PREFIX(gzclose)(out) != Z_OK) error("failed gzclose");
148 return Z_OK;
149 }
150 #endif /* USE_MMAP */
151
152 /* ===========================================================================
153 * Uncompress input to output then close both files.
154 */
gz_uncompress(gzFile in,FILE * out)155 void gz_uncompress(gzFile in, FILE *out) {
156 char *buf = (char *)malloc(BUFLENW);
157 int len;
158 int err;
159
160 if (buf == NULL) error("out of memory");
161
162 for (;;) {
163 len = PREFIX(gzread)(in, buf, BUFLENW);
164 if (len < 0) {
165 free(buf);
166 error(PREFIX(gzerror)(in, &err));
167 }
168 if (len == 0) break;
169
170 if ((int)fwrite(buf, 1, (unsigned)len, out) != len) {
171 free(buf);
172 error("failed fwrite");
173 }
174 }
175 free(buf);
176 if (fclose(out)) error("failed fclose");
177
178 if (PREFIX(gzclose)(in) != Z_OK) error("failed gzclose");
179 }
180
181
182 /* ===========================================================================
183 * Compress the given file: create a corresponding .gz file and remove the
184 * original.
185 */
file_compress(char * file,char * mode,int keep)186 void file_compress(char *file, char *mode, int keep) {
187 char outfile[MAX_NAME_LEN];
188 FILE *in;
189 gzFile out;
190
191 if (strlen(file) + strlen(GZ_SUFFIX) >= sizeof(outfile)) {
192 fprintf(stderr, "%s: filename too long\n", prog);
193 exit(1);
194 }
195
196 snprintf(outfile, sizeof(outfile), "%s%s", file, GZ_SUFFIX);
197
198 in = fopen(file, "rb");
199 if (in == NULL) {
200 perror(file);
201 exit(1);
202 }
203 out = PREFIX(gzopen)(outfile, mode);
204 if (out == NULL) {
205 fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile);
206 exit(1);
207 }
208 gz_compress(in, out);
209
210 if (!keep)
211 unlink(file);
212 }
213
214
215 /* ===========================================================================
216 * Uncompress the given file and remove the original.
217 */
file_uncompress(char * file,int keep)218 void file_uncompress(char *file, int keep) {
219 char buf[MAX_NAME_LEN];
220 char *infile, *outfile;
221 FILE *out;
222 gzFile in;
223 size_t len = strlen(file);
224
225 if (len + strlen(GZ_SUFFIX) >= sizeof(buf)) {
226 fprintf(stderr, "%s: filename too long\n", prog);
227 exit(1);
228 }
229
230 snprintf(buf, sizeof(buf), "%s", file);
231
232 if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) {
233 infile = file;
234 outfile = buf;
235 outfile[len-3] = '\0';
236 } else {
237 outfile = file;
238 infile = buf;
239 snprintf(buf + len, sizeof(buf) - len, "%s", GZ_SUFFIX);
240 }
241 in = PREFIX(gzopen)(infile, "rb");
242 if (in == NULL) {
243 fprintf(stderr, "%s: can't gzopen %s\n", prog, infile);
244 exit(1);
245 }
246 out = fopen(outfile, "wb");
247 if (out == NULL) {
248 perror(file);
249 exit(1);
250 }
251
252 gz_uncompress(in, out);
253
254 if (!keep)
255 unlink(infile);
256 }
257
show_help(void)258 void show_help(void) {
259 printf("Usage: minigzip [-c] [-d] [-k] [-f|-h|-R|-F|-T] [-A] [-0 to -9] [files...]\n\n" \
260 " -c : write to standard output\n" \
261 " -d : decompress\n" \
262 " -k : keep input files\n" \
263 " -f : compress with Z_FILTERED\n" \
264 " -h : compress with Z_HUFFMAN_ONLY\n" \
265 " -R : compress with Z_RLE\n" \
266 " -F : compress with Z_FIXED\n" \
267 " -T : stored raw\n" \
268 " -A : auto detect type\n" \
269 " -0 to -9 : compression level\n\n");
270 }
271
main(int argc,char * argv[])272 int main(int argc, char *argv[]) {
273 int copyout = 0;
274 int uncompr = 0;
275 int keep = 0;
276 int i = 0;
277 gzFile file;
278 char *bname, outmode[20];
279 char *strategy = "";
280 char *level = "6";
281 char *type = "b";
282
283 prog = argv[i];
284 bname = strrchr(argv[i], '/');
285 if (bname)
286 bname++;
287 else
288 bname = argv[i];
289
290 if (!strcmp(bname, "gunzip"))
291 uncompr = 1;
292 else if (!strcmp(bname, "zcat"))
293 copyout = uncompr = 1;
294
295 for (i = 1; i < argc; i++) {
296 if (strcmp(argv[i], "-c") == 0)
297 copyout = 1;
298 else if (strcmp(argv[i], "-d") == 0)
299 uncompr = 1;
300 else if (strcmp(argv[i], "-k") == 0)
301 keep = 1;
302 else if (strcmp(argv[i], "-A") == 0)
303 type = "";
304 else if (argv[i][0] == '-' && (argv[i][1] == 'f' || argv[i][1] == 'h' ||
305 argv[i][1] == 'R' || argv[i][1] == 'F' || argv[i][1] == 'T') && argv[i][2] == 0)
306 strategy = argv[i] + 1;
307 else if (argv[i][0] == '-' && argv[i][1] >= '0' && argv[i][1] <= '9' && argv[i][2] == 0)
308 level = argv[i] + 1;
309 else if (strcmp(argv[i], "--help") == 0) {
310 show_help();
311 return 0;
312 } else if (argv[i][0] == '-') {
313 show_help();
314 return 64; /* EX_USAGE */
315 } else {
316 break;
317 }
318 }
319
320 snprintf(outmode, sizeof(outmode), "w%s%s%s", type, strategy, level);
321
322 if (i == argc) {
323 SET_BINARY_MODE(stdin);
324 SET_BINARY_MODE(stdout);
325 if (uncompr) {
326 file = PREFIX(gzdopen)(fileno(stdin), "rb");
327 if (file == NULL) error("can't gzdopen stdin");
328 gz_uncompress(file, stdout);
329 } else {
330 file = PREFIX(gzdopen)(fileno(stdout), outmode);
331 if (file == NULL) error("can't gzdopen stdout");
332 gz_compress(stdin, file);
333 }
334 } else {
335 if (copyout) {
336 SET_BINARY_MODE(stdout);
337 }
338 do {
339 if (uncompr) {
340 if (copyout) {
341 file = PREFIX(gzopen)(argv[i], "rb");
342 if (file == NULL)
343 fprintf(stderr, "%s: can't gzopen %s\n", prog, argv[i]);
344 else
345 gz_uncompress(file, stdout);
346 } else {
347 file_uncompress(argv[i], keep);
348 }
349 } else {
350 if (copyout) {
351 FILE * in = fopen(argv[i], "rb");
352
353 if (in == NULL) {
354 perror(argv[i]);
355 } else {
356 file = PREFIX(gzdopen)(fileno(stdout), outmode);
357 if (file == NULL) error("can't gzdopen stdout");
358
359 gz_compress(in, file);
360 }
361
362 } else {
363 file_compress(argv[i], outmode, keep);
364 }
365 }
366 } while (++i < argc);
367 }
368 return 0;
369 }
370