summaryrefslogtreecommitdiff
path: root/src/stream.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/stream.c')
-rw-r--r--src/stream.c187
1 files changed, 187 insertions, 0 deletions
diff --git a/src/stream.c b/src/stream.c
new file mode 100644
index 0000000..286f41a
--- /dev/null
+++ b/src/stream.c
@@ -0,0 +1,187 @@
1// Standard C
2#include <errno.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6
7// System
8#include <unistd.h>
9
10// zlib
11#include <zlib.h>
12
13enum { BUFFER_SIZE = 8192 };
14
15static void
16usage(void)
17{
18 (void) fprintf(stderr, "compress [-d] infile outfile\n");
19}
20
21static void
22print_zlib_error(const char *operation, int error)
23{
24 if ( error == Z_ERRNO ) {
25 (void) fprintf(stderr, "%s error: %s\n", operation, strerror(errno));
26 }
27 else {
28 (void) fprintf(stderr, "%s error: %s\n", operation, zError(error));
29 }
30}
31
32int
33do_compress(FILE *in_file, FILE *out_file, int compress_level)
34{
35 z_stream strm = { 0 };
36
37 int error = deflateInit(&strm, compress_level);
38 if ( error ) {
39 return error;
40 }
41
42 unsigned char in_buffer[BUFFER_SIZE];
43 unsigned char out_buffer[BUFFER_SIZE];
44 int flush = Z_NO_FLUSH;
45
46 do {
47 strm.avail_in = fread(in_buffer, 1, BUFFER_SIZE, in_file);
48
49 if ( ferror(in_file) ) {
50 deflateEnd(&strm);
51 return Z_ERRNO;
52 }
53
54 flush = feof(in_file) ? Z_FINISH : Z_NO_FLUSH;
55 strm.next_in = in_buffer;
56
57 do {
58 strm.avail_out = BUFFER_SIZE;
59 strm.next_out = out_buffer;
60
61 error = deflate(&strm, flush);
62
63 size_t have = BUFFER_SIZE - strm.avail_out;
64 if ( fwrite(out_buffer, 1, have, out_file) != have || ferror(out_file) ) {
65 deflateEnd(&strm);
66 return Z_ERRNO;
67 }
68 } while ( strm.avail_out == 0 );
69 } while ( flush != Z_FINISH );
70
71 deflateEnd(&strm);
72 return Z_OK;
73}
74
75int
76do_uncompress(FILE *in_file, FILE *out_file)
77{
78 z_stream strm = { 0 };
79
80 int error = inflateInit(&strm);
81 if ( error ) {
82 return error;
83 }
84
85 unsigned char in_buffer[BUFFER_SIZE];
86 unsigned char out_buffer[BUFFER_SIZE];
87
88 do {
89 strm.avail_in = fread(in_buffer, 1, BUFFER_SIZE, in_file);
90
91 if ( ferror(in_file) ) {
92 inflateEnd(&strm);
93 return Z_ERRNO;
94 }
95
96 if ( strm.avail_in == 0 ) {
97 break;
98 }
99
100 strm.next_in = in_buffer;
101
102 do {
103 strm.avail_out = BUFFER_SIZE;
104 strm.next_out = out_buffer;
105
106 error = inflate(&strm, Z_NO_FLUSH);
107
108 switch ( error ) {
109 case Z_NEED_DICT:
110 error = Z_DATA_ERROR; /* and fall through */
111 case Z_DATA_ERROR:
112 case Z_MEM_ERROR:
113 (void) inflateEnd(&strm);
114 return error;
115 default:
116 break;
117 }
118
119 size_t have = BUFFER_SIZE - strm.avail_out;
120 if ( fwrite(out_buffer, 1, have, out_file) != have || ferror(out_file) ) {
121 (void) inflateEnd(&strm);
122 return Z_ERRNO;
123 }
124 } while ( strm.avail_out == 0 );
125 } while ( error != Z_STREAM_END );
126
127 inflateEnd(&strm);
128
129 return error == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
130}
131
132int
133main(int argc, char *argv[])
134{
135 enum {
136 COMPRESSION_STD = 5,
137 COMPRESSION_BEST = 9
138 };
139
140 int decompress = 0;
141 int compress_level = COMPRESSION_STD;
142
143 int opt = 0;
144 while ( (opt = getopt(argc, argv, "dl")) != -1 ) {
145 switch ( opt ) {
146 case 'd':
147 decompress = 1;
148 break;
149
150 case 'l':
151 compress_level = COMPRESSION_BEST;
152 break;
153
154 case '?':
155 default:
156 usage();
157 return EXIT_SUCCESS;
158 }
159 }
160 argc -= optind;
161 argv += optind;
162
163 if ( argc != 2 ) {
164 usage();
165 }
166
167 FILE *in_file = fopen(argv[0], "rb");
168 FILE *out_file = fopen(argv[1], "wb");
169
170 if ( !decompress ) {
171 int error = do_compress(in_file, out_file, compress_level);
172 if ( error ) {
173 print_zlib_error("compression", error);
174 }
175 }
176 else {
177 int error = do_uncompress(in_file, out_file);
178 if ( error ) {
179 print_zlib_error("extraction", error);
180 }
181 }
182
183 (void) fclose(out_file);
184 (void) fclose(in_file);
185
186 return EXIT_SUCCESS;
187}