1 /* minideflate.c -- test deflate/inflate under specific conditions
2 * Copyright (C) 2020 Nathan Moinvaziri
3 * For conditions of distribution and use, see copyright notice in zlib.h
4 */
5
6 #include <stdio.h>
7 #include <assert.h>
8
9 #include "zbuild.h"
10 #include "zutil.h"
11
12 #if defined(_WIN32) || defined(__CYGWIN__)
13 # include <fcntl.h>
14 # include <io.h>
15 # include <string.h>
16 # define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
17 # ifdef _MSC_VER
18 # define strcasecmp _stricmp
19 # endif
20 #else
21 # include <strings.h>
22 # define SET_BINARY_MODE(file)
23 #endif
24
25 #define CHECK_ERR(err, msg) { \
26 if (err != Z_OK) { \
27 fprintf(stderr, "%s error: %d\n", msg, err); \
28 exit(1); \
29 } \
30 }
31
32 /* Default read/write i/o buffer size based on GZBUFSIZE */
33 #define BUFSIZE 131072
34
35 /* ===========================================================================
36 * deflate() using specialized parameters
37 */
deflate_params(FILE * fin,FILE * fout,int32_t read_buf_size,int32_t write_buf_size,int32_t level,int32_t window_bits,int32_t mem_level,int32_t strategy,int32_t flush)38 void deflate_params(FILE *fin, FILE *fout, int32_t read_buf_size, int32_t write_buf_size, int32_t level,
39 int32_t window_bits, int32_t mem_level, int32_t strategy, int32_t flush) {
40 PREFIX3(stream) c_stream; /* compression stream */
41 uint8_t *read_buf;
42 uint8_t *write_buf;
43 int32_t read;
44 int err;
45
46 read_buf = (uint8_t *)malloc(read_buf_size);
47 if (read_buf == NULL) {
48 fprintf(stderr, "failed to create read buffer (%d)\n", read_buf_size);
49 return;
50 }
51 write_buf = (uint8_t *)malloc(write_buf_size);
52 if (write_buf == NULL) {
53 fprintf(stderr, "failed to create write buffer (%d)\n", write_buf_size);
54 free(read_buf);
55 return;
56 }
57
58 c_stream.zalloc = NULL;
59 c_stream.zfree = NULL;
60 c_stream.opaque = (void *)0;
61 c_stream.total_in = 0;
62 c_stream.total_out = 0;
63 c_stream.next_out = write_buf;
64 c_stream.avail_out = write_buf_size;
65
66 err = PREFIX(deflateInit2)(&c_stream, level, Z_DEFLATED, window_bits, mem_level, strategy);
67 CHECK_ERR(err, "deflateInit2");
68
69 /* Process input using our read buffer and flush type,
70 * output to stdout only once write buffer is full */
71 do {
72 read = (int32_t)fread(read_buf, 1, read_buf_size, fin);
73 if (read <= 0)
74 break;
75
76 c_stream.next_in = (z_const uint8_t *)read_buf;
77 c_stream.avail_in = read;
78
79 do {
80 err = PREFIX(deflate)(&c_stream, flush);
81 if (err == Z_STREAM_END) break;
82 CHECK_ERR(err, "deflate");
83
84 if (c_stream.next_out == write_buf + write_buf_size) {
85 fwrite(write_buf, 1, write_buf_size, fout);
86 c_stream.next_out = write_buf;
87 c_stream.avail_out = write_buf_size;
88 }
89 } while (c_stream.next_in < read_buf + read);
90 } while (err == Z_OK);
91
92 /* Finish the stream if necessary */
93 if (flush != Z_FINISH) {
94 c_stream.avail_in = 0;
95 do {
96 if (c_stream.next_out == write_buf + write_buf_size) {
97 fwrite(write_buf, 1, write_buf_size, fout);
98 c_stream.next_out = write_buf;
99 c_stream.avail_out = write_buf_size;
100 }
101
102 err = PREFIX(deflate)(&c_stream, Z_FINISH);
103 if (err == Z_STREAM_END) break;
104 CHECK_ERR(err, "deflate");
105 } while (1);
106 }
107
108 /* Output remaining data in write buffer */
109 if (c_stream.next_out != write_buf) {
110 fwrite(write_buf, 1, c_stream.next_out - write_buf, fout);
111 }
112
113 err = PREFIX(deflateEnd)(&c_stream);
114 CHECK_ERR(err, "deflateEnd");
115
116 free(read_buf);
117 free(write_buf);
118 }
119
120 /* ===========================================================================
121 * inflate() using specialized parameters
122 */
inflate_params(FILE * fin,FILE * fout,int32_t read_buf_size,int32_t write_buf_size,int32_t window_bits,int32_t flush)123 void inflate_params(FILE *fin, FILE *fout, int32_t read_buf_size, int32_t write_buf_size, int32_t window_bits,
124 int32_t flush) {
125 PREFIX3(stream) d_stream; /* decompression stream */
126 uint8_t *read_buf;
127 uint8_t *write_buf;
128 int32_t read;
129 int err;
130
131
132 read_buf = (uint8_t *)malloc(read_buf_size);
133 if (read_buf == NULL) {
134 fprintf(stderr, "failed to create read buffer (%d)\n", read_buf_size);
135 return;
136 }
137 write_buf = (uint8_t *)malloc(write_buf_size);
138 if (write_buf == NULL) {
139 fprintf(stderr, "failed to create write buffer (%d)\n", write_buf_size);
140 free(read_buf);
141 return;
142 }
143
144 d_stream.zalloc = NULL;
145 d_stream.zfree = NULL;
146 d_stream.opaque = (void *)0;
147 d_stream.total_in = 0;
148 d_stream.total_out = 0;
149 d_stream.next_out = write_buf;
150 d_stream.avail_out = write_buf_size;
151
152 err = PREFIX(inflateInit2)(&d_stream, window_bits);
153 CHECK_ERR(err, "inflateInit2");
154
155 /* Process input using our read buffer and flush type,
156 * output to stdout only once write buffer is full */
157 do {
158 read = (int32_t)fread(read_buf, 1, read_buf_size, fin);
159 if (read <= 0)
160 break;
161
162 d_stream.next_in = (z_const uint8_t *)read_buf;
163 d_stream.avail_in = read;
164
165 do {
166 err = PREFIX(inflate)(&d_stream, flush);
167 if (err == Z_STREAM_END) break;
168 CHECK_ERR(err, "inflate");
169
170 if (d_stream.next_out == write_buf + write_buf_size) {
171 fwrite(write_buf, 1, write_buf_size, fout);
172 d_stream.next_out = write_buf;
173 d_stream.avail_out = write_buf_size;
174 }
175 } while (d_stream.next_in < read_buf + read);
176 } while (err == Z_OK);
177
178 /* Finish the stream if necessary */
179 if (flush != Z_FINISH) {
180 d_stream.avail_in = 0;
181 do {
182 if (d_stream.next_out == write_buf + write_buf_size) {
183 fwrite(write_buf, 1, write_buf_size, fout);
184 d_stream.next_out = write_buf;
185 d_stream.avail_out = write_buf_size;
186 }
187
188 err = PREFIX(inflate)(&d_stream, Z_FINISH);
189 if (err == Z_STREAM_END) break;
190 CHECK_ERR(err, "inflate");
191 } while (1);
192 }
193
194 /* Output remaining data in write buffer */
195 if (d_stream.next_out != write_buf) {
196 fwrite(write_buf, 1, d_stream.next_out - write_buf, fout);
197 }
198
199 err = PREFIX(inflateEnd)(&d_stream);
200 CHECK_ERR(err, "inflateEnd");
201
202 free(read_buf);
203 free(write_buf);
204 }
205
show_help(void)206 void show_help(void) {
207 printf("Usage: minideflate [-c][-d][-k] [-f|-h|-R|-F] [-m level] [-r/-t size] [-s flush] [-w bits] [-0 to -9] [input file]\n\n" \
208 " -c : write to standard output\n" \
209 " -d : decompress\n" \
210 " -k : keep input file\n" \
211 " -f : compress with Z_FILTERED\n" \
212 " -h : compress with Z_HUFFMAN_ONLY\n" \
213 " -R : compress with Z_RLE\n" \
214 " -F : compress with Z_FIXED\n" \
215 " -m : memory level (1 to 8)\n" \
216 " -w : window bits..\n" \
217 " : -1 to -15 for raw deflate\n"
218 " : 0 to 15 for deflate (adler32)\n"
219 " : 16 to 31 for gzip (crc32)\n"
220 " -s : flush type (0 to 5)\n" \
221 " -r : read buffer size\n" \
222 " -t : write buffer size\n" \
223 " -0 to -9 : compression level\n\n");
224 }
225
main(int argc,char ** argv)226 int main(int argc, char **argv) {
227 int32_t i;
228 int32_t mem_level = DEF_MEM_LEVEL;
229 int32_t window_bits = INT32_MAX;
230 int32_t strategy = Z_DEFAULT_STRATEGY;
231 int32_t level = Z_DEFAULT_COMPRESSION;
232 int32_t read_buf_size = BUFSIZE;
233 int32_t write_buf_size = BUFSIZE;
234 int32_t flush = Z_NO_FLUSH;
235 uint8_t copyout = 0;
236 uint8_t uncompr = 0;
237 uint8_t keep = 0;
238 FILE *fin = stdin;
239 FILE *fout = stdout;
240
241
242 if (argc == 1) {
243 show_help();
244 return 64; /* EX_USAGE */
245 }
246
247 for (i = 1; i < argc; i++) {
248 if ((strcmp(argv[i], "-m") == 0) && (i + 1 < argc))
249 mem_level = atoi(argv[++i]);
250 else if ((strcmp(argv[i], "-w") == 0) && (i + 1 < argc))
251 window_bits = atoi(argv[++i]);
252 else if ((strcmp(argv[i], "-r") == 0) && (i + 1 < argc))
253 read_buf_size = atoi(argv[++i]);
254 else if ((strcmp(argv[i], "-t") == 0) && (i + 1 < argc))
255 write_buf_size = atoi(argv[++i]);
256 else if ((strcmp(argv[i], "-s") == 0) && (i + 1 < argc))
257 flush = atoi(argv[++i]);
258 else if (strcmp(argv[i], "-c") == 0)
259 copyout = 1;
260 else if (strcmp(argv[i], "-d") == 0)
261 uncompr = 1;
262 else if (strcmp(argv[i], "-k") == 0)
263 keep = 1;
264 else if (strcmp(argv[i], "-f") == 0)
265 strategy = Z_FILTERED;
266 else if (strcmp(argv[i], "-F") == 0)
267 strategy = Z_FIXED;
268 else if (strcmp(argv[i], "-h") == 0)
269 strategy = Z_HUFFMAN_ONLY;
270 else if (strcmp(argv[i], "-R") == 0)
271 strategy = Z_RLE;
272 else if (argv[i][0] == '-' && argv[i][1] >= '0' && argv[i][1] <= '9' && argv[i][2] == 0)
273 level = argv[i][1] - '0';
274 else if (strcmp(argv[i], "--help") == 0) {
275 show_help();
276 return 0;
277 } else if (argv[i][0] == '-') {
278 show_help();
279 return 64; /* EX_USAGE */
280 } else
281 break;
282 }
283
284 SET_BINARY_MODE(stdin);
285 SET_BINARY_MODE(stdout);
286
287 if (i != argc) {
288 fin = fopen(argv[i], "rb+");
289 if (fin == NULL) {
290 fprintf(stderr, "Failed to open file: %s\n", argv[i]);
291 exit(1);
292 }
293 if (!copyout) {
294 char *out_file = (char *)calloc(1, strlen(argv[i]) + 6);
295 if (out_file == NULL) {
296 fprintf(stderr, "Not enough memory\n");
297 exit(1);
298 }
299 strcat(out_file, argv[i]);
300 if (!uncompr) {
301 if (window_bits < 0) {
302 strcat(out_file, ".zraw");
303 } else if (window_bits > MAX_WBITS) {
304 strcat(out_file, ".gz");
305 } else {
306 strcat(out_file, ".z");
307 }
308 } else {
309 char *out_ext = strrchr(out_file, '.');
310 if (out_ext != NULL) {
311 if (strcasecmp(out_ext, ".zraw") == 0 && window_bits == INT32_MAX) {
312 fprintf(stderr, "Must specify window bits for raw deflate stream\n");
313 exit(1);
314 }
315 *out_ext = 0;
316 }
317 }
318 fout = fopen(out_file, "wb");
319 if (fout == NULL) {
320 fprintf(stderr, "Failed to open file: %s\n", out_file);
321 exit(1);
322 }
323 free(out_file);
324 }
325 }
326
327 if (window_bits == INT32_MAX) {
328 window_bits = MAX_WBITS;
329 /* Auto-detect wrapper for inflateInit */
330 if (uncompr)
331 window_bits += 32;
332 }
333
334 if (window_bits == INT32_MAX) {
335 window_bits = MAX_WBITS;
336 /* Auto-detect wrapper for inflateInit */
337 if (uncompr)
338 window_bits += 32;
339 }
340
341 if (uncompr) {
342 inflate_params(fin, fout, read_buf_size, write_buf_size, window_bits, flush);
343 } else {
344 deflate_params(fin, fout, read_buf_size, write_buf_size, level, window_bits, mem_level, strategy, flush);
345 }
346
347 if (fin != stdin) {
348 fclose(fin);
349 if (!copyout && !keep) {
350 unlink(argv[i]);
351 }
352 }
353 if (fout != stdout) {
354 fclose(fout);
355 }
356
357 return 0;
358 }
359