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