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