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