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