1 /* Compresses a user-specified number of chunks from stdin into stdout as a single gzip stream.
2  * Each chunk is compressed with a user-specified level.
3  */
4 
5 #include "zbuild.h"
6 #ifdef ZLIB_COMPAT
7 #  include "zlib.h"
8 #else
9 #  include "zlib-ng.h"
10 #endif
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 
16 #if defined(_WIN32) || defined(__CYGWIN__)
17 #  include <fcntl.h>
18 #  include <io.h>
19 #  define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
20 #else
21 #  define SET_BINARY_MODE(file)
22 #endif
23 
read_all(unsigned char * buf,size_t size)24 static int read_all(unsigned char *buf, size_t size) {
25     size_t total_read = 0;
26     while (total_read < size) {
27         size_t n_read = fread(buf + total_read, 1, size - total_read, stdin);
28         if (ferror(stdin)) {
29             perror("fread\n");
30             return 1;
31         }
32         if (n_read == 0) {
33             fprintf(stderr, "Premature EOF\n");
34             return 1;
35         }
36         total_read += n_read;
37     }
38     return 0;
39 }
40 
write_all(unsigned char * buf,size_t size)41 static int write_all(unsigned char *buf, size_t size) {
42     size_t total_written = 0;
43     while (total_written < size) {
44         size_t n_written = fwrite(buf + total_written, 1, size - total_written, stdout);
45         if (ferror(stdout)) {
46             perror("fwrite\n");
47             return 1;
48         }
49         total_written += n_written;
50     }
51     return 0;
52 }
53 
compress_chunk(PREFIX3 (stream)* strm,int level,int size,int last)54 static int compress_chunk(PREFIX3(stream) *strm, int level, int size, int last) {
55     int ret = 1;
56     int err = 0;
57     unsigned long compsize;
58     unsigned char *buf;
59 
60     if (size <= 0) {
61         fprintf(stderr, "compress_chunk() invalid size %d\n", size);
62         goto done;
63     }
64     if (level < 0 || level > 9) {
65         fprintf(stderr, "compress_chunk() invalid level %d\n", level);
66         goto done;
67     }
68 
69     compsize = 100 + 2 * PREFIX(deflateBound)(strm, size);
70     buf = malloc(size + compsize);
71     if (buf == NULL) {
72         fprintf(stderr, "Out of memory\n");
73         goto done;
74     }
75     if (read_all(buf, size) != 0) {
76         goto free_buf;
77     }
78 
79     /* Provide only output buffer to deflateParams(). It might need some space to flush the leftovers from the last
80      * deflate(), but we don't want it to compress anything new. */
81     strm->next_in = NULL;
82     strm->avail_in = 0;
83     strm->next_out = buf + size;
84     strm->avail_out = compsize;
85     err = PREFIX(deflateParams)(strm, level, Z_DEFAULT_STRATEGY);
86     if (err != Z_OK) {
87         fprintf(stderr, "deflateParams() failed with code %d\n", err);
88         goto free_buf;
89     }
90 
91     /* Provide input buffer to deflate(). */
92     strm->next_in = buf;
93     strm->avail_in = size;
94     err = PREFIX(deflate)(strm, last ? Z_FINISH : Z_SYNC_FLUSH);
95     if ((!last && err != Z_OK) || (last && err != Z_STREAM_END)) {
96         fprintf(stderr, "deflate() failed with code %d\n", err);
97         goto free_buf;
98     }
99     if (strm->avail_in != 0) {
100         fprintf(stderr, "deflate() did not consume %d bytes of input\n", strm->avail_in);
101         goto free_buf;
102     }
103     if (write_all(buf + size, compsize - strm->avail_out) != 0) {
104         goto free_buf;
105     }
106     ret = 0;
107 
108 free_buf:
109     free(buf);
110 done:
111     return ret;
112 }
113 
show_help(void)114 void show_help(void)
115 {
116     printf("Usage: switchlevels [-w bits] level1 size1 [level2 size2 ...]\n\n" \
117            "  -w : window bits (8 to 15 for gzip, -8 to -15 for zlib)\n\n");
118 }
119 
main(int argc,char ** argv)120 int main(int argc, char **argv) {
121     int ret = EXIT_FAILURE;
122     int err = 0;
123     int size = 0;
124     int level = Z_DEFAULT_COMPRESSION;
125     int level_arg = 1;
126     int window_bits = MAX_WBITS + 16;
127     PREFIX3(stream) strm;
128 
129 
130     if ((argc == 1) || (argc == 2 && strcmp(argv[1], "--help") == 0)) {
131         show_help();
132         return 0;
133     }
134 
135     SET_BINARY_MODE(stdin);
136     SET_BINARY_MODE(stdout);
137 
138     memset(&strm, 0, sizeof(strm));
139 
140     for (int i = 1; i < argc - 1; i++) {
141         if (strcmp(argv[i], "-w") == 0 && i+1 < argc) {
142             window_bits = atoi(argv[++i]);
143         } else {
144             level_arg = i;
145             level = atoi(argv[i]);
146             break;
147         }
148     }
149 
150     err = PREFIX(deflateInit2)(&strm, level, Z_DEFLATED, window_bits, 8, Z_DEFAULT_STRATEGY);
151     if (err != Z_OK) {
152         fprintf(stderr, "deflateInit() failed with code %d\n", err);
153         goto done;
154     }
155 
156     for (int i = level_arg; i < argc - 1; i += 2) {
157         level = atoi(argv[i]);
158         size = atoi(argv[i + 1]);
159         if (compress_chunk(&strm, level, size, i + 2 >= argc - 1) != 0) {
160             goto deflate_end;
161         }
162     }
163     ret = EXIT_SUCCESS;
164 
165 deflate_end:
166     PREFIX(deflateEnd)(&strm);
167 done:
168     return ret;
169 }
170