1 /*
2 * Copyright 2014, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7 #include <errno.h>
8 #include <getopt.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12
13 #include <File.h>
14
15 #include <ZlibCompressionAlgorithm.h>
16 #include <ZstdCompressionAlgorithm.h>
17
18
19 extern const char* __progname;
20 const char* kCommandName = __progname;
21
22
23 enum CompressionType {
24 ZlibCompression,
25 GzipCompression,
26 ZstdCompression,
27 };
28
29
30 static const char* kUsage =
31 "Usage: %s <options> <input file> <output file>\n"
32 "Compresses or decompresses (option -d) a file.\n"
33 "\n"
34 "Options:\n"
35 " -0 ... -9\n"
36 " Use compression level 0 ... 9. 0 means no, 9 best compression.\n"
37 " Defaults to 9.\n"
38 " -d, --decompress\n"
39 " Decompress the input file (default is compress).\n"
40 " -f <format>\n"
41 " Specify the compression format: \"zlib\" (default), \"gzip\"\n"
42 " or \"zstd\".\n"
43 " -h, --help\n"
44 " Print this usage info.\n"
45 " -i, --input-stream\n"
46 " Use the input stream API (default is output stream API).\n"
47 ;
48
49
50 static void
print_usage_and_exit(bool error)51 print_usage_and_exit(bool error)
52 {
53 fprintf(error ? stderr : stdout, kUsage, kCommandName);
54 exit(error ? 1 : 0);
55 }
56
57
58 int
main(int argc,const char * const * argv)59 main(int argc, const char* const* argv)
60 {
61 int compressionLevel = -1;
62 bool compress = true;
63 bool useInputStream = false;
64 CompressionType compressionType = ZlibCompression;
65
66 while (true) {
67 static struct option sLongOptions[] = {
68 { "decompress", no_argument, 0, 'd' },
69 { "help", no_argument, 0, 'h' },
70 { "input-stream", no_argument, 0, 'i' },
71 { 0, 0, 0, 0 }
72 };
73
74 opterr = 0; // don't print errors
75 int c = getopt_long(argc, (char**)argv, "+0123456789df:hi",
76 sLongOptions, NULL);
77 if (c == -1)
78 break;
79
80 switch (c) {
81 case '0':
82 case '1':
83 case '2':
84 case '3':
85 case '4':
86 case '5':
87 case '6':
88 case '7':
89 case '8':
90 case '9':
91 compressionLevel = c - '0';
92 break;
93
94 case 'h':
95 print_usage_and_exit(false);
96 break;
97
98 case 'd':
99 compress = false;
100 break;
101
102 case 'f':
103 if (strcmp(optarg, "zlib") == 0) {
104 compressionType = ZlibCompression;
105 } else if (strcmp(optarg, "gzip") == 0) {
106 compressionType = GzipCompression;
107 } else if (strcmp(optarg, "zstd") == 0) {
108 compressionType = ZstdCompression;
109 } else {
110 fprintf(stderr, "Error: Unsupported compression type "
111 "\"%s\"\n", optarg);
112 return 1;
113 }
114 break;
115
116 case 'i':
117 useInputStream = true;
118 break;
119
120 default:
121 print_usage_and_exit(true);
122 break;
123 }
124 }
125
126 // The remaining arguments are input and output file.
127 if (optind + 2 != argc)
128 print_usage_and_exit(true);
129
130 const char* inputFilePath = argv[optind++];
131 const char* outputFilePath = argv[optind++];
132
133 // open input file
134 BFile inputFile;
135 status_t error = inputFile.SetTo(inputFilePath, B_READ_ONLY);
136 if (error != B_OK) {
137 fprintf(stderr, "Error: Failed to open \"%s\": %s\n", inputFilePath,
138 strerror(errno));
139 return 1;
140 }
141
142 // open output file
143 BFile outputFile;
144 error = outputFile.SetTo(outputFilePath,
145 B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
146 if (error != B_OK) {
147 fprintf(stderr, "Error: Failed to open \"%s\": %s\n", outputFilePath,
148 strerror(errno));
149 return 1;
150 }
151
152 // create compression algorithm and parameters
153 BCompressionAlgorithm* compressionAlgorithm;
154 BCompressionParameters* compressionParameters;
155 BDecompressionParameters* decompressionParameters;
156 switch (compressionType) {
157 case ZlibCompression:
158 case GzipCompression:
159 {
160 if (compressionLevel < 0)
161 compressionLevel = B_ZLIB_COMPRESSION_DEFAULT;
162 compressionAlgorithm = new BZlibCompressionAlgorithm;
163 BZlibCompressionParameters* zlibCompressionParameters
164 = new BZlibCompressionParameters(compressionLevel);
165 zlibCompressionParameters->SetGzipFormat(
166 compressionType == GzipCompression);
167 compressionParameters = zlibCompressionParameters;
168 decompressionParameters = new BZlibDecompressionParameters;
169 break;
170 }
171 case ZstdCompression:
172 {
173 if (compressionLevel < 0)
174 compressionLevel = B_ZSTD_COMPRESSION_DEFAULT;
175 compressionAlgorithm = new BZstdCompressionAlgorithm;
176 compressionParameters
177 = new BZstdCompressionParameters(compressionLevel);
178 decompressionParameters = new BZstdDecompressionParameters;
179 break;
180 }
181 }
182
183 if (useInputStream) {
184 // create input stream
185 BDataIO* inputStream;
186 if (compress) {
187 error = compressionAlgorithm->CreateCompressingInputStream(
188 &inputFile, compressionParameters, inputStream);
189 } else {
190 error = compressionAlgorithm->CreateDecompressingInputStream(
191 &inputFile, decompressionParameters, inputStream);
192 }
193
194 if (error != B_OK) {
195 fprintf(stderr, "Error: Failed to create input stream: %s\n",
196 strerror(error));
197 return 1;
198 }
199
200 // processing loop
201 for (;;) {
202 uint8 buffer[64 * 1024];
203 ssize_t bytesRead = inputStream->Read(buffer, sizeof(buffer));
204 if (bytesRead < 0) {
205 fprintf(stderr, "Error: Failed to read from input stream: %s\n",
206 strerror(bytesRead));
207 return 1;
208 }
209 if (bytesRead == 0)
210 break;
211
212 error = outputFile.WriteExactly(buffer, bytesRead);
213 if (error != B_OK) {
214 fprintf(stderr, "Error: Failed to write to output file: %s\n",
215 strerror(error));
216 return 1;
217 }
218 }
219 } else {
220 // create output stream
221 BDataIO* outputStream;
222 if (compress) {
223 error = compressionAlgorithm->CreateCompressingOutputStream(
224 &outputFile, compressionParameters, outputStream);
225 } else {
226 error = compressionAlgorithm->CreateDecompressingOutputStream(
227 &outputFile, decompressionParameters, outputStream);
228 }
229
230 if (error != B_OK) {
231 fprintf(stderr, "Error: Failed to create output stream: %s\n",
232 strerror(error));
233 return 1;
234 }
235
236 // processing loop
237 for (;;) {
238 uint8 buffer[64 * 1024];
239 ssize_t bytesRead = inputFile.Read(buffer, sizeof(buffer));
240 if (bytesRead < 0) {
241 fprintf(stderr, "Error: Failed to read from input file: %s\n",
242 strerror(bytesRead));
243 return 1;
244 }
245 if (bytesRead == 0)
246 break;
247
248 error = outputStream->WriteExactly(buffer, bytesRead);
249 if (error != B_OK) {
250 fprintf(stderr, "Error: Failed to write to output stream: %s\n",
251 strerror(error));
252 return 1;
253 }
254 }
255
256 // flush the output stream
257 error = outputStream->Flush();
258 if (error != B_OK) {
259 fprintf(stderr, "Error: Failed to flush output stream: %s\n",
260 strerror(error));
261 return 1;
262 }
263 }
264
265 return 0;
266 }
267