Home | History | Annotate | Download | only in bsdiff
      1 // Copyright 2017 The Chromium OS Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "bsdiff/bsdiff_arguments.h"
      6 
      7 #include <getopt.h>
      8 
      9 #include <algorithm>
     10 #include <iostream>
     11 
     12 #include "brotli/encode.h"
     13 
     14 using std::endl;
     15 using std::string;
     16 
     17 namespace {
     18 
     19 // The name in string for the compression algorithms.
     20 constexpr char kNoCompressionString[] = "nocompression";
     21 constexpr char kBZ2String[] = "bz2";
     22 constexpr char kBrotliString[] = "brotli";
     23 
     24 // The name in string for the bsdiff format.
     25 constexpr char kLegacyString[] = "legacy";
     26 constexpr char kBsdf2String[] = "bsdf2";
     27 constexpr char kBsdiff40String[] = "bsdiff40";
     28 constexpr char kEndsleyString[] = "endsley";
     29 
     30 const struct option OPTIONS[] = {
     31     {"format", required_argument, nullptr, 0},
     32     {"minlen", required_argument, nullptr, 0},
     33     {"type", required_argument, nullptr, 0},
     34     {"quality", required_argument, nullptr, 0},
     35     {nullptr, 0, nullptr, 0},
     36 };
     37 
     38 const uint32_t kBrotliDefaultQuality = BROTLI_MAX_QUALITY;
     39 
     40 }  // namespace
     41 
     42 namespace bsdiff {
     43 
     44 bool BsdiffArguments::IsValid() const {
     45   if (compressor_type_ == CompressorType::kBrotli &&
     46       (compression_quality_ < BROTLI_MIN_QUALITY ||
     47        compression_quality_ > BROTLI_MAX_QUALITY)) {
     48     return false;
     49   }
     50 
     51   if (format_ == BsdiffFormat::kLegacy) {
     52     return compressor_type_ == CompressorType::kBZ2;
     53   } else if (format_ == BsdiffFormat::kBsdf2) {
     54     return (compressor_type_ == CompressorType::kBZ2 ||
     55             compressor_type_ == CompressorType::kBrotli);
     56   } else if (format_ == BsdiffFormat::kEndsley) {
     57     // All compression options are valid for this format.
     58     return true;
     59   }
     60   return false;
     61 }
     62 
     63 bool BsdiffArguments::ParseCommandLine(int argc, char** argv) {
     64   int opt;
     65   int option_index;
     66   while ((opt = getopt_long(argc, argv, "", OPTIONS, &option_index)) != -1) {
     67     if (opt != 0) {
     68       return false;
     69     }
     70 
     71     string name = OPTIONS[option_index].name;
     72     if (name == "format") {
     73       if (!ParseBsdiffFormat(optarg, &format_)) {
     74         return false;
     75       }
     76     } else if (name == "minlen") {
     77       if (!ParseMinLength(optarg, &min_length_)) {
     78         return false;
     79       }
     80     } else if (name == "type") {
     81       if (!ParseCompressorType(optarg, &compressor_type_)) {
     82         return false;
     83       }
     84     } else if (name == "quality") {
     85       if (!ParseQuality(optarg, &compression_quality_)) {
     86         return false;
     87       }
     88     } else {
     89       std::cerr << "Unrecognized options: " << name << endl;
     90       return false;
     91     }
     92   }
     93 
     94   // If quality is uninitialized for brotli, set it to default value.
     95   if (format_ != BsdiffFormat::kLegacy &&
     96       compressor_type_ == CompressorType::kBrotli &&
     97       compression_quality_ == -1) {
     98     compression_quality_ = kBrotliDefaultQuality;
     99   } else if (compressor_type_ != CompressorType::kBrotli &&
    100              compression_quality_ != -1) {
    101     std::cerr << "Warning: Compression quality is only used in the brotli"
    102                  " compressor." << endl;
    103   }
    104 
    105   return true;
    106 }
    107 
    108 bool BsdiffArguments::ParseCompressorType(const string& str,
    109                                           CompressorType* type) {
    110   string type_string = str;
    111   std::transform(type_string.begin(), type_string.end(), type_string.begin(),
    112                  ::tolower);
    113   if (type_string == kNoCompressionString) {
    114     *type = CompressorType::kNoCompression;
    115     return true;
    116   } else if (type_string == kBZ2String) {
    117     *type = CompressorType::kBZ2;
    118     return true;
    119   } else if (type_string == kBrotliString) {
    120     *type = CompressorType::kBrotli;
    121     return true;
    122   }
    123   std::cerr << "Failed to parse compressor type in " << str << endl;
    124   return false;
    125 }
    126 
    127 bool BsdiffArguments::ParseMinLength(const string& str, size_t* len) {
    128   errno = 0;
    129   char* end;
    130   const char* s = str.c_str();
    131   long result = strtol(s, &end, 10);
    132   if (errno != 0 || s == end || *end != '\0') {
    133     return false;
    134   }
    135 
    136   if (result < 0) {
    137     std::cerr << "Minimum length must be non-negative: " << str << endl;
    138     return false;
    139   }
    140 
    141   *len = result;
    142   return true;
    143 }
    144 
    145 bool BsdiffArguments::ParseBsdiffFormat(const string& str,
    146                                         BsdiffFormat* format) {
    147   string format_string = str;
    148   std::transform(format_string.begin(), format_string.end(),
    149                  format_string.begin(), ::tolower);
    150   if (format_string == kLegacyString || format_string == kBsdiff40String) {
    151     *format = BsdiffFormat::kLegacy;
    152     return true;
    153   } else if (format_string == kBsdf2String) {
    154     *format = BsdiffFormat::kBsdf2;
    155     return true;
    156   } else if (format_string == kEndsleyString) {
    157     *format = BsdiffFormat::kEndsley;
    158     return true;
    159   }
    160   std::cerr << "Failed to parse bsdiff format in " << str << endl;
    161   return false;
    162 }
    163 
    164 bool BsdiffArguments::ParseQuality(const string& str, int* quality) {
    165   errno = 0;
    166   char* end;
    167   const char* s = str.c_str();
    168   long result = strtol(s, &end, 10);
    169   if (errno != 0 || s == end || *end != '\0') {
    170     return false;
    171   }
    172 
    173   if (result < BROTLI_MIN_QUALITY || result > BROTLI_MAX_QUALITY) {
    174     std::cerr << "Compression quality out of range " << str << endl;
    175     return false;
    176   }
    177 
    178   *quality = result;
    179   return true;
    180 }
    181 
    182 }  // namespace bsdiff
    183