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 #ifndef UNALIGNED_OK
36 # include <malloc.h>
37 #endif
38
39 #if defined(_WIN32) || defined(__CYGWIN__)
40 # include <fcntl.h>
41 # include <io.h>
42 # define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
43 #else
44 # define SET_BINARY_MODE(file)
45 #endif
46
47 #if defined(_MSC_VER) && _MSC_VER < 1900
48 # define snprintf _snprintf
49 #endif
50
51 #if !defined(Z_HAVE_UNISTD_H) && !defined(_LARGEFILE64_SOURCE)
52 #ifndef _WIN32 /* unlink already in stdio.h for Win32 */
53 extern int unlink (const char *);
54 #endif
55 #endif
56
57 #ifndef GZ_SUFFIX
58 # define GZ_SUFFIX ".gz"
59 #endif
60 #define SUFFIX_LEN (sizeof(GZ_SUFFIX)-1)
61
62 #ifndef BUFLEN
63 # define BUFLEN 16384 /* read buffer size */
64 #endif
65 #define BUFLENW (BUFLEN * 3) /* write buffer size */
66 #define MAX_NAME_LEN 1024
67
68 static char *prog;
69
70 void error (const char *msg);
71 void gz_compress (FILE *in, gzFile out);
72 #ifdef USE_MMAP
73 int gz_compress_mmap (FILE *in, gzFile out);
74 #endif
75 void gz_uncompress (gzFile in, FILE *out);
76 void file_compress (char *file, char *mode, int keep);
77 void file_uncompress (char *file, int keep);
78 int main (int argc, char *argv[]);
79
80 /* ===========================================================================
81 * Display error message and exit
82 */
error(const char * msg)83 void error(const char *msg) {
84 fprintf(stderr, "%s: %s\n", prog, msg);
85 exit(1);
86 }
87
88 /* ===========================================================================
89 * Compress input to output then close both files.
90 */
91
gz_compress(FILE * in,gzFile out)92 void gz_compress(FILE *in, gzFile out) {
93 char *buf = (char *)calloc(BUFLEN, 1);
94 int len;
95 int err;
96
97 #ifdef USE_MMAP
98 /* Try first compressing with mmap. If mmap fails (minigzip used in a
99 * pipe), use the normal fread loop.
100 */
101 if (gz_compress_mmap(in, out) == Z_OK) return;
102 #endif
103 for (;;) {
104 len = (int)fread(buf, 1, BUFLEN, in);
105 if (ferror(in)) {
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 <malbrech@eso.org> */
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 for (;;) {
161 len = PREFIX(gzread)(in, buf, BUFLENW);
162 if (len < 0) error (PREFIX(gzerror)(in, &err));
163 if (len == 0) break;
164
165 if ((int)fwrite(buf, 1, (unsigned)len, out) != len) {
166 error("failed fwrite");
167 }
168 }
169 free(buf);
170 if (fclose(out)) error("failed fclose");
171
172 if (PREFIX(gzclose)(in) != Z_OK) error("failed gzclose");
173 }
174
175
176 /* ===========================================================================
177 * Compress the given file: create a corresponding .gz file and remove the
178 * original.
179 */
file_compress(char * file,char * mode,int keep)180 void file_compress(char *file, char *mode, int keep) {
181 char outfile[MAX_NAME_LEN];
182 FILE *in;
183 gzFile out;
184
185 if (strlen(file) + strlen(GZ_SUFFIX) >= sizeof(outfile)) {
186 fprintf(stderr, "%s: filename too long\n", prog);
187 exit(1);
188 }
189
190 snprintf(outfile, sizeof(outfile), "%s%s", file, GZ_SUFFIX);
191
192 in = fopen(file, "rb");
193 if (in == NULL) {
194 perror(file);
195 exit(1);
196 }
197 out = PREFIX(gzopen)(outfile, mode);
198 if (out == NULL) {
199 fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile);
200 exit(1);
201 }
202 gz_compress(in, out);
203
204 if (!keep)
205 unlink(file);
206 }
207
208
209 /* ===========================================================================
210 * Uncompress the given file and remove the original.
211 */
file_uncompress(char * file,int keep)212 void file_uncompress(char *file, int keep) {
213 char buf[MAX_NAME_LEN];
214 char *infile, *outfile;
215 FILE *out;
216 gzFile in;
217 size_t len = strlen(file);
218
219 if (len + strlen(GZ_SUFFIX) >= sizeof(buf)) {
220 fprintf(stderr, "%s: filename too long\n", prog);
221 exit(1);
222 }
223
224 snprintf(buf, sizeof(buf), "%s", file);
225
226 if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) {
227 infile = file;
228 outfile = buf;
229 outfile[len-3] = '\0';
230 } else {
231 outfile = file;
232 infile = buf;
233 snprintf(buf + len, sizeof(buf) - len, "%s", GZ_SUFFIX);
234 }
235 in = PREFIX(gzopen)(infile, "rb");
236 if (in == NULL) {
237 fprintf(stderr, "%s: can't gzopen %s\n", prog, infile);
238 exit(1);
239 }
240 out = fopen(outfile, "wb");
241 if (out == NULL) {
242 perror(file);
243 exit(1);
244 }
245
246 gz_uncompress(in, out);
247
248 if (!keep)
249 unlink(infile);
250 }
251
show_help(void)252 void show_help(void) {
253 printf("Usage: minigzip [-c] [-d] [-k] [-f|-h|-R|-F|-T] [-A] [-0 to -9] [files...]\n\n" \
254 " -c : write to standard output\n" \
255 " -d : decompress\n" \
256 " -k : keep input files\n" \
257 " -f : compress with Z_FILTERED\n" \
258 " -h : compress with Z_HUFFMAN_ONLY\n" \
259 " -R : compress with Z_RLE\n" \
260 " -F : compress with Z_FIXED\n" \
261 " -T : stored raw\n" \
262 " -A : auto detect type\n" \
263 " -0 to -9 : compression level\n\n");
264 }
265
main(int argc,char * argv[])266 int main(int argc, char *argv[]) {
267 int copyout = 0;
268 int uncompr = 0;
269 int keep = 0;
270 int i = 0;
271 gzFile file;
272 char *bname, outmode[20];
273 char *strategy = "";
274 char *level = "6";
275 char *type = "b";
276
277 prog = argv[i];
278 bname = strrchr(argv[i], '/');
279 if (bname)
280 bname++;
281 else
282 bname = argv[i];
283
284 if (!strcmp(bname, "gunzip"))
285 uncompr = 1;
286 else if (!strcmp(bname, "zcat"))
287 copyout = uncompr = 1;
288
289 for (i = 1; i < argc; i++) {
290 if (strcmp(argv[i], "-c") == 0)
291 copyout = 1;
292 else if (strcmp(argv[i], "-d") == 0)
293 uncompr = 1;
294 else if (strcmp(argv[i], "-k") == 0)
295 keep = 1;
296 else if (strcmp(argv[i], "-A") == 0)
297 type = "";
298 else if (argv[i][0] == '-' && (argv[i][1] == 'f' || argv[i][1] == 'h' ||
299 argv[i][1] == 'R' || argv[i][1] == 'F' || argv[i][1] == 'T') && argv[i][2] == 0)
300 strategy = argv[i] + 1;
301 else if (argv[i][0] == '-' && argv[i][1] >= '0' && argv[i][1] <= '9' && argv[i][2] == 0)
302 level = argv[i] + 1;
303 else if (strcmp(argv[i], "--help") == 0) {
304 show_help();
305 return 0;
306 } else if (argv[i][0] == '-') {
307 show_help();
308 return 64; /* EX_USAGE */
309 } else {
310 break;
311 }
312 }
313
314 snprintf(outmode, sizeof(outmode), "w%s%s%s", type, strategy, level);
315
316 if (i == argc) {
317 SET_BINARY_MODE(stdin);
318 SET_BINARY_MODE(stdout);
319 if (uncompr) {
320 file = PREFIX(gzdopen)(fileno(stdin), "rb");
321 if (file == NULL) error("can't gzdopen stdin");
322 gz_uncompress(file, stdout);
323 } else {
324 file = PREFIX(gzdopen)(fileno(stdout), outmode);
325 if (file == NULL) error("can't gzdopen stdout");
326 gz_compress(stdin, file);
327 }
328 } else {
329 if (copyout) {
330 SET_BINARY_MODE(stdout);
331 }
332 do {
333 if (uncompr) {
334 if (copyout) {
335 file = PREFIX(gzopen)(argv[i], "rb");
336 if (file == NULL)
337 fprintf(stderr, "%s: can't gzopen %s\n", prog, argv[i]);
338 else
339 gz_uncompress(file, stdout);
340 } else {
341 file_uncompress(argv[i], keep);
342 }
343 } else {
344 if (copyout) {
345 FILE * in = fopen(argv[i], "rb");
346
347 if (in == NULL) {
348 perror(argv[i]);
349 } else {
350 file = PREFIX(gzdopen)(fileno(stdout), outmode);
351 if (file == NULL) error("can't gzdopen stdout");
352
353 gz_compress(in, file);
354 }
355
356 } else {
357 file_compress(argv[i], outmode, keep);
358 }
359 }
360 } while (++i < argc);
361 }
362 return 0;
363 }
364