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 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 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