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