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