| OLD | NEW |
| (Empty) |
| 1 // Copyright 2008 Google Inc. | |
| 2 // Author: Lincoln Smith | |
| 3 // | |
| 4 // Licensed under the Apache License, Version 2.0 (the "License"); | |
| 5 // you may not use this file except in compliance with the License. | |
| 6 // You may obtain a copy of the License at | |
| 7 // | |
| 8 // http://www.apache.org/licenses/LICENSE-2.0 | |
| 9 // | |
| 10 // Unless required by applicable law or agreed to in writing, software | |
| 11 // distributed under the License is distributed on an "AS IS" BASIS, | |
| 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 13 // See the License for the specific language governing permissions and | |
| 14 // limitations under the License. | |
| 15 // | |
| 16 // A command-line interface to the open-vcdiff library. | |
| 17 | |
| 18 #include <config.h> | |
| 19 #include <cassert> | |
| 20 #include <cerrno> | |
| 21 #include <cstdio> | |
| 22 #include <cstring> // strerror | |
| 23 #include <memory> | |
| 24 #include <string> | |
| 25 #include <vector> | |
| 26 #include "google/gflags.h" | |
| 27 #include "logging.h" | |
| 28 #include "google/vcdecoder.h" | |
| 29 #include "google/vcencoder.h" | |
| 30 | |
| 31 using std::string; | |
| 32 using google::GetCommandLineFlagInfoOrDie; | |
| 33 using google::ShowUsageWithFlagsRestrict; | |
| 34 | |
| 35 // The buffer size, which determines the maximum allowable size | |
| 36 // of a target window, based on how much memory can be allocated. | |
| 37 // Both of these can be increased (and the default can be decreased) | |
| 38 // using the --buffersize flag. | |
| 39 static const size_t kDefaultBufferSize = 1 << 20; // 1 MB | |
| 40 static const size_t kMaxBufferSize = 1 << 26; // 64 MB | |
| 41 | |
| 42 // Definitions of command-line flags | |
| 43 DEFINE_string(dictionary, "", | |
| 44 "File containing dictionary data (required)"); | |
| 45 DEFINE_string(target, "", | |
| 46 "Target file (default is stdin for encode, stdout for decode"); | |
| 47 DEFINE_string(delta, "", | |
| 48 "Encoded delta file (default is stdout for encode, " | |
| 49 "stdin for decode"); | |
| 50 DEFINE_uint64(buffersize, kDefaultBufferSize, | |
| 51 "Buffer size for reading input file"); | |
| 52 DEFINE_bool(checksum, false, | |
| 53 "Include an Adler32 checksum of the target data when encoding"); | |
| 54 DEFINE_bool(interleaved, false, "Use interleaved format"); | |
| 55 DEFINE_bool(stats, false, "Report compression percentage"); | |
| 56 DEFINE_bool(target_matches, false, "Find duplicate strings in target data" | |
| 57 " as well as dictionary data"); | |
| 58 | |
| 59 static const char* const kUsageString = | |
| 60 " {encode | delta | decode | patch }[ <options> ]\n" | |
| 61 "encode or delta: create delta file from dictionary and target file\n" | |
| 62 "decode or patch: reconstruct target file from dictionary and delta file"; | |
| 63 | |
| 64 namespace open_vcdiff { | |
| 65 | |
| 66 class VCDiffFileBasedCoder { | |
| 67 public: | |
| 68 VCDiffFileBasedCoder(); | |
| 69 ~VCDiffFileBasedCoder(); | |
| 70 | |
| 71 // Once the command-line arguments have been parsed, these functions | |
| 72 // will use the supplied options to carry out a file-based encode | |
| 73 // or decode operation. | |
| 74 bool Encode(); | |
| 75 bool Decode(); | |
| 76 bool DecodeAndCompare(); // for "vcdiff test"; compare target with original | |
| 77 | |
| 78 private: | |
| 79 // Determines the size of the file. The given file must be an input file | |
| 80 // opened for reading only, not an input stream such as stdin. The function | |
| 81 // returns true and populates file_size if successful; otherwise, it returns | |
| 82 // false. | |
| 83 static bool FileSize(FILE* file, size_t* file_size); | |
| 84 | |
| 85 // Opens a file for incremental reading. file_name is the name of the file | |
| 86 // to be opened. file_type should be a descriptive name (like "target") for | |
| 87 // use in log messages. If successful, returns true and sets *file to a | |
| 88 // valid input file, *buffer to a region of memory allocated using malloc() | |
| 89 // (so the caller must release it using free()), and buffer_size to the size | |
| 90 // of the buffer, which will not be larger than the size of the file, and | |
| 91 // will not be smaller than the --buffersize option. If the function fails, | |
| 92 // it outputs a log message and returns false. | |
| 93 bool OpenFileForReading(const string& file_name, | |
| 94 const char* file_type, | |
| 95 FILE** file, | |
| 96 std::vector<char>* buffer); | |
| 97 | |
| 98 // Opens the dictionary file and reads it into a newly allocated buffer. | |
| 99 // If successful, returns true and populates dictionary_ with the dictionary | |
| 100 // contents; otherwise, returns false. | |
| 101 bool OpenDictionary(); | |
| 102 | |
| 103 // Opens the input file (the delta or target file) for reading. | |
| 104 // Allocates space for the input buffer. If successful, | |
| 105 // input_file_ will be valid and input_buffer_ will be allocated. | |
| 106 bool OpenInputFile() { | |
| 107 return OpenFileForReading(input_file_name_, | |
| 108 input_file_type_, | |
| 109 &input_file_, | |
| 110 &input_buffer_); | |
| 111 } | |
| 112 | |
| 113 // Opens the output file (the target or delta file) for writing. | |
| 114 // If successful, output_file_ will be valid. | |
| 115 bool OpenOutputFile(); | |
| 116 | |
| 117 // Opens the output file (the target file) for comparison against the decoded | |
| 118 // output when using "vcdiff test". | |
| 119 bool OpenOutputFileForCompare() { | |
| 120 return OpenFileForReading(output_file_name_, | |
| 121 output_file_type_, | |
| 122 &output_file_, | |
| 123 &compare_buffer_); | |
| 124 } | |
| 125 | |
| 126 // Reads as much input data as possible from the input file | |
| 127 // into input_buffer_. If successful, returns true and sets *bytes_read | |
| 128 // to the number of bytes read into input_buffer_. If an error occurs, | |
| 129 // writes an error log message and returns false. | |
| 130 bool ReadInput(size_t* bytes_read); | |
| 131 | |
| 132 // Writes the contents of output to output_file_. If successful, returns | |
| 133 // true. If an error occurs, writes an error log message and returns false. | |
| 134 bool WriteOutput(const string& output); | |
| 135 | |
| 136 // Reads a number of bytes from output_file_ equal to the size of output, | |
| 137 // and compares to make sure they match the contents of output. If the bytes | |
| 138 // do not match, or if end of file is reached before the expected number of | |
| 139 // bytes have been read, or a read error occurs, the function returns false; | |
| 140 // otherwise, returns true. | |
| 141 bool CompareOutput(const string& output); | |
| 142 | |
| 143 // Dictionary contents. The entire dictionary file will be read into memory. | |
| 144 std::vector<char> dictionary_; | |
| 145 | |
| 146 std::auto_ptr<open_vcdiff::HashedDictionary> hashed_dictionary_; | |
| 147 | |
| 148 // These should be set to either "delta" or "target". They are only | |
| 149 // used in log messages such as "Error opening delta file..." | |
| 150 const char* input_file_type_; | |
| 151 const char* output_file_type_; | |
| 152 | |
| 153 // The filenames used for input and output. Will be empty if stdin | |
| 154 // or stdout is being used. | |
| 155 string input_file_name_; | |
| 156 string output_file_name_; | |
| 157 | |
| 158 // stdio-style file handles for the input and output files and the dictionary. | |
| 159 // When encoding, input_file_ is the target file and output_file_ is the delta | |
| 160 // file; when decoding, the reverse is true. The dictionary is always read | |
| 161 // from a file rather than from standard input. | |
| 162 FILE* input_file_; | |
| 163 FILE* output_file_; | |
| 164 | |
| 165 // A memory buffer used to load the input file into memory. If the input | |
| 166 // comes from stdin because no input file was specified, then the size of | |
| 167 // input_buffer_ will be the value specified by the --buffersize option. | |
| 168 // If the input comes from a file, then the buffer will be allocated to match | |
| 169 // the file size, if possible. However, the buffer will not exceed | |
| 170 // kMaxBufferSize bytes in length, unless the user specifies the --buffersize | |
| 171 // option to override that limit. | |
| 172 std::vector<char> input_buffer_; | |
| 173 | |
| 174 // A memory buffer used to load the output file into memory for comparison | |
| 175 // if "vcdiff test" is specified. | |
| 176 std::vector<char> compare_buffer_; | |
| 177 | |
| 178 // Making these private avoids implicit copy constructor & assignment operator | |
| 179 VCDiffFileBasedCoder(const VCDiffFileBasedCoder&); // NOLINT | |
| 180 void operator=(const VCDiffFileBasedCoder&); | |
| 181 }; | |
| 182 | |
| 183 inline VCDiffFileBasedCoder::VCDiffFileBasedCoder() | |
| 184 : input_file_type_(""), | |
| 185 output_file_type_(""), | |
| 186 input_file_(NULL), | |
| 187 output_file_(NULL) { } | |
| 188 | |
| 189 VCDiffFileBasedCoder::~VCDiffFileBasedCoder() { | |
| 190 if (input_file_ && (input_file_ != stdin)) { | |
| 191 fclose(input_file_); | |
| 192 input_file_ = NULL; | |
| 193 } | |
| 194 if (output_file_ && (output_file_ != stdout)) { | |
| 195 fclose(output_file_); | |
| 196 output_file_ = NULL; | |
| 197 } | |
| 198 } | |
| 199 | |
| 200 bool VCDiffFileBasedCoder::FileSize(FILE* file, size_t* file_size) { | |
| 201 long initial_position = ftell(file); | |
| 202 if (fseek(file, 0, SEEK_END) != 0) { | |
| 203 return false; | |
| 204 } | |
| 205 *file_size = static_cast<size_t>(ftell(file)); | |
| 206 if (fseek(file, initial_position, SEEK_SET) != 0) { | |
| 207 return false; | |
| 208 } | |
| 209 return true; | |
| 210 } | |
| 211 | |
| 212 bool VCDiffFileBasedCoder::OpenDictionary() { | |
| 213 assert(dictionary_.empty()); | |
| 214 assert(!FLAGS_dictionary.empty()); | |
| 215 FILE* dictionary_file = fopen(FLAGS_dictionary.c_str(), "rb"); | |
| 216 if (!dictionary_file) { | |
| 217 LOG(ERROR) << "Error opening dictionary file '" << FLAGS_dictionary | |
| 218 << "': " << strerror(errno) << LOG_ENDL; | |
| 219 return false; | |
| 220 } | |
| 221 size_t dictionary_size = 0U; | |
| 222 if (!FileSize(dictionary_file, &dictionary_size)) { | |
| 223 LOG(ERROR) << "Error finding size of dictionary file '" << FLAGS_dictionary | |
| 224 << "': " << strerror(errno) << LOG_ENDL; | |
| 225 return false; | |
| 226 } | |
| 227 dictionary_.resize(dictionary_size); | |
| 228 if (fread(&dictionary_[0], 1, dictionary_size, dictionary_file) | |
| 229 != dictionary_size) { | |
| 230 LOG(ERROR) << "Unable to read dictionary file '" << FLAGS_dictionary | |
| 231 << "': " << strerror(errno) << LOG_ENDL; | |
| 232 fclose(dictionary_file); | |
| 233 dictionary_.clear(); | |
| 234 return false; | |
| 235 } | |
| 236 fclose(dictionary_file); | |
| 237 return true; | |
| 238 } | |
| 239 | |
| 240 bool VCDiffFileBasedCoder::OpenFileForReading(const string& file_name, | |
| 241 const char* file_type, | |
| 242 FILE** file, | |
| 243 std::vector<char>* buffer) { | |
| 244 assert(buffer->empty()); | |
| 245 size_t buffer_size = 0U; | |
| 246 if (!*file && file_name.empty()) { | |
| 247 *file = stdin; | |
| 248 buffer_size = static_cast<size_t>(FLAGS_buffersize); | |
| 249 } else { | |
| 250 if (!*file) { | |
| 251 *file = fopen(file_name.c_str(), "rb"); | |
| 252 if (!*file) { | |
| 253 LOG(ERROR) << "Error opening " << file_type << " file '" | |
| 254 << file_name << "': " << strerror(errno) << LOG_ENDL; | |
| 255 return false; | |
| 256 } | |
| 257 } | |
| 258 size_t file_size = 0U; | |
| 259 if (!FileSize(*file, &file_size)) { | |
| 260 LOG(ERROR) << "Error finding size of " << file_type << " file '" | |
| 261 << file_name << "': " << strerror(errno) << LOG_ENDL; | |
| 262 return false; | |
| 263 } | |
| 264 buffer_size = kMaxBufferSize; | |
| 265 if (FLAGS_buffersize > buffer_size) { | |
| 266 buffer_size = static_cast<size_t>(FLAGS_buffersize); | |
| 267 } | |
| 268 if (file_size < buffer_size) { | |
| 269 // Allocate just enough memory to store the entire file | |
| 270 buffer_size = file_size; | |
| 271 } | |
| 272 } | |
| 273 buffer->resize(buffer_size); | |
| 274 return true; | |
| 275 } | |
| 276 | |
| 277 // Opens the output file for streamed read operations using the | |
| 278 // standard C I/O library, i.e., fopen(), fwrite(), fclose(). | |
| 279 // No output buffer is allocated because the encoded/decoded output | |
| 280 // is constructed progressively using a std::string object | |
| 281 // whose buffer is resized as needed. | |
| 282 bool VCDiffFileBasedCoder::OpenOutputFile() { | |
| 283 if (output_file_name_.empty()) { | |
| 284 output_file_ = stdout; | |
| 285 } else { | |
| 286 output_file_ = fopen(output_file_name_.c_str(), "wb"); | |
| 287 if (!output_file_) { | |
| 288 LOG(ERROR) << "Error opening " << output_file_type_ << " file '" | |
| 289 << output_file_name_ | |
| 290 << "': " << strerror(errno) << LOG_ENDL; | |
| 291 return false; | |
| 292 } | |
| 293 } | |
| 294 return true; | |
| 295 } | |
| 296 | |
| 297 bool VCDiffFileBasedCoder::ReadInput(size_t* bytes_read) { | |
| 298 // Read from file or stdin | |
| 299 *bytes_read = fread(&input_buffer_[0], 1, input_buffer_.size(), input_file_); | |
| 300 if (ferror(input_file_)) { | |
| 301 LOG(ERROR) << "Error reading from " << input_file_type_ << " file '" | |
| 302 << input_file_name_ | |
| 303 << "': " << strerror(errno) << LOG_ENDL; | |
| 304 return false; | |
| 305 } | |
| 306 return true; | |
| 307 } | |
| 308 | |
| 309 bool VCDiffFileBasedCoder::WriteOutput(const string& output) { | |
| 310 if (!output.empty()) { | |
| 311 // Some new output has been generated and is ready to be written | |
| 312 // to the output file or to stdout. | |
| 313 fwrite(output.data(), 1, output.size(), output_file_); | |
| 314 if (ferror(output_file_)) { | |
| 315 LOG(ERROR) << "Error writing " << output.size() << " bytes to " | |
| 316 << output_file_type_ << " file '" << output_file_name_ | |
| 317 << "': " << strerror(errno) << LOG_ENDL; | |
| 318 return false; | |
| 319 } | |
| 320 } | |
| 321 return true; | |
| 322 } | |
| 323 | |
| 324 bool VCDiffFileBasedCoder::CompareOutput(const string& output) { | |
| 325 if (!output.empty()) { | |
| 326 size_t output_size = output.size(); | |
| 327 // Some new output has been generated and is ready to be compared against | |
| 328 // the output file. | |
| 329 if (output_size > compare_buffer_.size()) { | |
| 330 compare_buffer_.resize(output_size); | |
| 331 } | |
| 332 size_t bytes_read = fread(&compare_buffer_[0], | |
| 333 1, | |
| 334 output_size, | |
| 335 output_file_); | |
| 336 if (ferror(output_file_)) { | |
| 337 LOG(ERROR) << "Error reading from " << output_file_type_ << " file '" | |
| 338 << output_file_name_ << "': " << strerror(errno) << LOG_ENDL; | |
| 339 return false; | |
| 340 } | |
| 341 if (bytes_read < output_size) { | |
| 342 LOG(ERROR) << "Decoded target is longer than original target file" | |
| 343 << LOG_ENDL; | |
| 344 return false; | |
| 345 } | |
| 346 if (output.compare(0, output_size, &compare_buffer_[0], bytes_read) != 0) { | |
| 347 LOG(ERROR) << "Original target file does not match decoded target" | |
| 348 << LOG_ENDL; | |
| 349 return false; | |
| 350 } | |
| 351 } | |
| 352 return true; | |
| 353 } | |
| 354 | |
| 355 bool VCDiffFileBasedCoder::Encode() { | |
| 356 input_file_type_ = "target"; | |
| 357 input_file_name_ = FLAGS_target; | |
| 358 output_file_type_ = "delta"; | |
| 359 output_file_name_ = FLAGS_delta; | |
| 360 if (!OpenDictionary() || !OpenInputFile() || !OpenOutputFile()) { | |
| 361 return false; | |
| 362 } | |
| 363 hashed_dictionary_.reset( | |
| 364 new open_vcdiff::HashedDictionary(&dictionary_[0], dictionary_.size())); | |
| 365 if (!hashed_dictionary_->Init()) { | |
| 366 LOG(ERROR) << "Error initializing hashed dictionary" << LOG_ENDL; | |
| 367 return false; | |
| 368 } | |
| 369 VCDiffFormatExtensionFlags format_flags = open_vcdiff::VCD_STANDARD_FORMAT; | |
| 370 if (FLAGS_interleaved) { | |
| 371 format_flags |= open_vcdiff::VCD_FORMAT_INTERLEAVED; | |
| 372 } | |
| 373 if (FLAGS_checksum) { | |
| 374 format_flags |= open_vcdiff::VCD_FORMAT_CHECKSUM; | |
| 375 } | |
| 376 open_vcdiff::VCDiffStreamingEncoder encoder(hashed_dictionary_.get(), | |
| 377 format_flags, | |
| 378 FLAGS_target_matches); | |
| 379 string output; | |
| 380 size_t input_size = 0; | |
| 381 size_t output_size = 0; | |
| 382 { | |
| 383 if (!encoder.StartEncoding(&output)) { | |
| 384 LOG(ERROR) << "Error during encoder initialization" << LOG_ENDL; | |
| 385 return false; | |
| 386 } | |
| 387 } | |
| 388 do { | |
| 389 size_t bytes_read = 0; | |
| 390 if (!WriteOutput(output) || !ReadInput(&bytes_read)) { | |
| 391 return false; | |
| 392 } | |
| 393 output_size += output.size(); | |
| 394 output.clear(); | |
| 395 if (bytes_read > 0) { | |
| 396 input_size += bytes_read; | |
| 397 if (!encoder.EncodeChunk(&input_buffer_[0], bytes_read, &output)) { | |
| 398 LOG(ERROR) << "Error trying to encode data chunk of length " | |
| 399 << bytes_read << LOG_ENDL; | |
| 400 return false; | |
| 401 } | |
| 402 } | |
| 403 } while (!feof(input_file_)); | |
| 404 encoder.FinishEncoding(&output); | |
| 405 if (!WriteOutput(output)) { | |
| 406 return false; | |
| 407 } | |
| 408 output_size += output.size(); | |
| 409 output.clear(); | |
| 410 if (FLAGS_stats && (input_size > 0)) { | |
| 411 LOG(INFO) << "Original size: " << input_size | |
| 412 << "\tCompressed size: " << output_size << " (" | |
| 413 << ((static_cast<double>(output_size) / input_size) * 100) | |
| 414 << "% of original)" << LOG_ENDL; | |
| 415 } | |
| 416 return true; | |
| 417 } | |
| 418 | |
| 419 bool VCDiffFileBasedCoder::Decode() { | |
| 420 input_file_type_ = "delta"; | |
| 421 input_file_name_ = FLAGS_delta; | |
| 422 output_file_type_ = "target"; | |
| 423 output_file_name_ = FLAGS_target; | |
| 424 if (!OpenDictionary() || !OpenInputFile() || !OpenOutputFile()) { | |
| 425 return false; | |
| 426 } | |
| 427 | |
| 428 open_vcdiff::VCDiffStreamingDecoder decoder; | |
| 429 string output; | |
| 430 size_t input_size = 0; | |
| 431 size_t output_size = 0; | |
| 432 decoder.StartDecoding(&dictionary_[0], dictionary_.size()); | |
| 433 | |
| 434 do { | |
| 435 size_t bytes_read = 0; | |
| 436 if (!ReadInput(&bytes_read)) { | |
| 437 return false; | |
| 438 } | |
| 439 if (bytes_read > 0) { | |
| 440 input_size += bytes_read; | |
| 441 if (!decoder.DecodeChunk(&input_buffer_[0], bytes_read, &output)) { | |
| 442 LOG(ERROR) << "Error trying to decode data chunk of length " | |
| 443 << bytes_read << LOG_ENDL; | |
| 444 return false; | |
| 445 } | |
| 446 } | |
| 447 if (!WriteOutput(output)) { | |
| 448 return false; | |
| 449 } | |
| 450 output_size += output.size(); | |
| 451 output.clear(); | |
| 452 } while (!feof(input_file_)); | |
| 453 if (!decoder.FinishDecoding()) { | |
| 454 LOG(ERROR) << "Decode error; '" << FLAGS_delta | |
| 455 << " may not be a valid VCDIFF delta file" << LOG_ENDL; | |
| 456 return false; | |
| 457 } | |
| 458 if (!WriteOutput(output)) { | |
| 459 return false; | |
| 460 } | |
| 461 output_size += output.size(); | |
| 462 output.clear(); | |
| 463 if (FLAGS_stats && (output_size > 0)) { | |
| 464 LOG(INFO) << "Decompressed size: " << output_size | |
| 465 << "\tCompressed size: " << input_size << " (" | |
| 466 << ((static_cast<double>(input_size) / output_size) * 100) | |
| 467 << "% of original)" << LOG_ENDL; | |
| 468 } | |
| 469 return true; | |
| 470 } | |
| 471 | |
| 472 bool VCDiffFileBasedCoder::DecodeAndCompare() { | |
| 473 input_file_type_ = "delta"; | |
| 474 input_file_name_ = FLAGS_delta; | |
| 475 output_file_type_ = "target"; | |
| 476 output_file_name_ = FLAGS_target; | |
| 477 if (!OpenDictionary() || !OpenInputFile() || !OpenOutputFileForCompare()) { | |
| 478 return false; | |
| 479 } | |
| 480 | |
| 481 open_vcdiff::VCDiffStreamingDecoder decoder; | |
| 482 string output; | |
| 483 size_t input_size = 0; | |
| 484 size_t output_size = 0; | |
| 485 decoder.StartDecoding(&dictionary_[0], dictionary_.size()); | |
| 486 | |
| 487 do { | |
| 488 size_t bytes_read = 0; | |
| 489 if (!ReadInput(&bytes_read)) { | |
| 490 return false; | |
| 491 } | |
| 492 if (bytes_read > 0) { | |
| 493 input_size += bytes_read; | |
| 494 if (!decoder.DecodeChunk(&input_buffer_[0], bytes_read, &output)) { | |
| 495 LOG(ERROR) << "Error trying to decode data chunk of length " | |
| 496 << bytes_read << LOG_ENDL; | |
| 497 return false; | |
| 498 } | |
| 499 } | |
| 500 if (!CompareOutput(output)) { | |
| 501 return false; | |
| 502 } | |
| 503 output_size += output.size(); | |
| 504 output.clear(); | |
| 505 } while (!feof(input_file_)); | |
| 506 if (!decoder.FinishDecoding()) { | |
| 507 LOG(ERROR) << "Decode error; '" << FLAGS_delta | |
| 508 << " may not be a valid VCDIFF delta file" << LOG_ENDL; | |
| 509 return false; | |
| 510 } | |
| 511 if (!CompareOutput(output)) { | |
| 512 return false; | |
| 513 } | |
| 514 output_size += output.size(); | |
| 515 output.clear(); | |
| 516 if (fgetc(output_file_) != EOF) { | |
| 517 LOG(ERROR) << "Decoded target is shorter than original target file" | |
| 518 << LOG_ENDL; | |
| 519 return false; | |
| 520 } | |
| 521 if (ferror(output_file_)) { | |
| 522 LOG(ERROR) << "Error reading end-of-file indicator from target file" | |
| 523 << LOG_ENDL; | |
| 524 return false; | |
| 525 } | |
| 526 if (FLAGS_stats && (output_size > 0)) { | |
| 527 LOG(INFO) << "Decompressed size: " << output_size | |
| 528 << "\tCompressed size: " << input_size << " (" | |
| 529 << ((static_cast<double>(input_size) / output_size) * 100) | |
| 530 << "% of original)" << LOG_ENDL; | |
| 531 } | |
| 532 return true; | |
| 533 } | |
| 534 | |
| 535 } // namespace open_vcdiff | |
| 536 | |
| 537 int main(int argc, char** argv) { | |
| 538 const char* const command_name = argv[0]; | |
| 539 google::SetUsageMessage(kUsageString); | |
| 540 google::ParseCommandLineFlags(&argc, &argv, true); | |
| 541 if (argc != 2) { | |
| 542 LOG(ERROR) << command_name << ": Must specify exactly one command option" | |
| 543 << LOG_ENDL; | |
| 544 ShowUsageWithFlagsRestrict(command_name, "vcdiff"); | |
| 545 return 1; | |
| 546 } | |
| 547 const char* const command_option = argv[1]; | |
| 548 if (FLAGS_dictionary.empty()) { | |
| 549 LOG(ERROR) << command_name << " " << command_option | |
| 550 << ": Must specify --dictionary <file-name>" << LOG_ENDL; | |
| 551 ShowUsageWithFlagsRestrict(command_name, "vcdiff"); | |
| 552 return 1; | |
| 553 } | |
| 554 if (!GetCommandLineFlagInfoOrDie("buffersize").is_default && | |
| 555 (FLAGS_buffersize == 0)) { | |
| 556 LOG(ERROR) << command_name << ": Option --buffersize cannot be 0" | |
| 557 << LOG_ENDL; | |
| 558 ShowUsageWithFlagsRestrict(command_name, "vcdiff"); | |
| 559 return 1; | |
| 560 } | |
| 561 if ((strcmp(command_option, "encode") == 0) || | |
| 562 (strcmp(command_option, "delta") == 0)) { | |
| 563 open_vcdiff::VCDiffFileBasedCoder coder; | |
| 564 if (!coder.Encode()) { | |
| 565 return 1; | |
| 566 } | |
| 567 // The destructor for VCDiffFileBasedCoder will clean up the open files | |
| 568 // and allocated memory. | |
| 569 } else if ((strcmp(command_option, "decode") == 0) || | |
| 570 (strcmp(command_option, "patch") == 0)) { | |
| 571 open_vcdiff::VCDiffFileBasedCoder coder; | |
| 572 if (!coder.Decode()) { | |
| 573 return 1; | |
| 574 } | |
| 575 } else if ((strcmp(command_option, "test") == 0)) { | |
| 576 // "vcdiff test" does not appear in the usage string, but can be | |
| 577 // used for debugging. It encodes, then decodes, then compares the result | |
| 578 // with the original target. It expects the same arguments as | |
| 579 // "vcdiff encode", with the additional requirement that the --target | |
| 580 // and --delta file arguments must be specified, rather than using stdin | |
| 581 // or stdout. It produces a delta file just as for "vcdiff encode". | |
| 582 if (FLAGS_target.empty() || FLAGS_delta.empty()) { | |
| 583 LOG(ERROR) << command_name | |
| 584 << " test: Must specify both --target <file-name>" | |
| 585 " and --delta <file-name>" << LOG_ENDL; | |
| 586 return 1; | |
| 587 } | |
| 588 const string original_target(FLAGS_target); | |
| 589 // Put coder into a separate scope. | |
| 590 { | |
| 591 open_vcdiff::VCDiffFileBasedCoder coder; | |
| 592 if (!coder.Encode()) { | |
| 593 return 1; | |
| 594 } | |
| 595 } | |
| 596 { | |
| 597 open_vcdiff::VCDiffFileBasedCoder coder; | |
| 598 if (!coder.DecodeAndCompare()) { | |
| 599 return 1; | |
| 600 } | |
| 601 } | |
| 602 } else { | |
| 603 LOG(ERROR) << command_name << ": Unrecognized command option " | |
| 604 << command_option << LOG_ENDL; | |
| 605 ShowUsageWithFlagsRestrict(command_name, "vcdiff"); | |
| 606 return 1; | |
| 607 } | |
| 608 return 0; | |
| 609 } | |
| OLD | NEW |