blob: 20e61497331bc12e1e97774cc7b9ce7c62e47e46 [file] [log] [blame]
Alex Deymo4fd22ea2018-02-15 16:13:28 +01001// 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
Tianjie Xu1f1cdb22017-11-20 11:05:55 -08005#include "bsdiff/bsdiff_arguments.h"
6
7#include <getopt.h>
8
9#include <algorithm>
10#include <iostream>
11
12#include "brotli/encode.h"
13
14using std::endl;
15using std::string;
16
17namespace {
18
19// The name in string for the compression algorithms.
20constexpr char kNoCompressionString[] = "nocompression";
21constexpr char kBZ2String[] = "bz2";
22constexpr char kBrotliString[] = "brotli";
23
24// The name in string for the bsdiff format.
25constexpr char kLegacyString[] = "legacy";
26constexpr char kBsdf2String[] = "bsdf2";
27constexpr char kBsdiff40String[] = "bsdiff40";
Alex Deymoe790a3b2017-11-09 18:09:11 +010028constexpr char kEndsleyString[] = "endsley";
Tianjie Xu1f1cdb22017-11-20 11:05:55 -080029
30const struct option OPTIONS[] = {
31 {"format", required_argument, nullptr, 0},
Alex Deymo383f6772018-02-08 15:50:11 +010032 {"minlen", required_argument, nullptr, 0},
Tianjie Xu1f1cdb22017-11-20 11:05:55 -080033 {"type", required_argument, nullptr, 0},
Tianjie Xu2e70b552018-03-02 16:22:10 -080034 {"brotli_quality", required_argument, nullptr, 0},
Tianjie Xu1f1cdb22017-11-20 11:05:55 -080035 {nullptr, 0, nullptr, 0},
36};
37
38const uint32_t kBrotliDefaultQuality = BROTLI_MAX_QUALITY;
39
40} // namespace
41
42namespace bsdiff {
43
Tianjie Xu77833b62018-03-07 18:13:47 -080044std::vector<CompressorType> BsdiffArguments::compressor_types() const {
45 return std::vector<CompressorType>(compressor_types_.begin(),
46 compressor_types_.end());
47}
48
Tianjie Xu1f1cdb22017-11-20 11:05:55 -080049bool BsdiffArguments::IsValid() const {
Tianjie Xu77833b62018-03-07 18:13:47 -080050 if (compressor_types_.empty()) {
51 return false;
52 }
53
54 if (IsCompressorSupported(CompressorType::kBrotli) &&
Tianjie Xu2e70b552018-03-02 16:22:10 -080055 (brotli_quality_ < BROTLI_MIN_QUALITY ||
56 brotli_quality_ > BROTLI_MAX_QUALITY)) {
Alex Deymoe790a3b2017-11-09 18:09:11 +010057 return false;
58 }
59
Tianjie Xu1f1cdb22017-11-20 11:05:55 -080060 if (format_ == BsdiffFormat::kLegacy) {
Tianjie Xu77833b62018-03-07 18:13:47 -080061 return compressor_types_.size() == 1 &&
62 IsCompressorSupported(CompressorType::kBZ2);
Tianjie Xu1f1cdb22017-11-20 11:05:55 -080063 } else if (format_ == BsdiffFormat::kBsdf2) {
Tianjie Xu77833b62018-03-07 18:13:47 -080064 if (IsCompressorSupported(CompressorType::kNoCompression)) {
65 std::cerr << "no compression is not supported in Bsdf2 format\n";
66 return false;
67 }
Alex Deymoe790a3b2017-11-09 18:09:11 +010068 return true;
Tianjie Xu77833b62018-03-07 18:13:47 -080069 } else if (format_ == BsdiffFormat::kEndsley) {
70 // Only one compressor is supported for this format.
71 return compressor_types_.size() == 1;
Tianjie Xu1f1cdb22017-11-20 11:05:55 -080072 }
73 return false;
74}
75
76bool BsdiffArguments::ParseCommandLine(int argc, char** argv) {
77 int opt;
78 int option_index;
79 while ((opt = getopt_long(argc, argv, "", OPTIONS, &option_index)) != -1) {
80 if (opt != 0) {
81 return false;
82 }
83
Alex Deymo383f6772018-02-08 15:50:11 +010084 string name = OPTIONS[option_index].name;
Tianjie Xu1f1cdb22017-11-20 11:05:55 -080085 if (name == "format") {
86 if (!ParseBsdiffFormat(optarg, &format_)) {
87 return false;
88 }
Alex Deymo383f6772018-02-08 15:50:11 +010089 } else if (name == "minlen") {
90 if (!ParseMinLength(optarg, &min_length_)) {
91 return false;
92 }
Tianjie Xu1f1cdb22017-11-20 11:05:55 -080093 } else if (name == "type") {
Tianjie Xu77833b62018-03-07 18:13:47 -080094 if (!ParseCompressorTypes(optarg, &compressor_types_)) {
Tianjie Xu1f1cdb22017-11-20 11:05:55 -080095 return false;
96 }
Tianjie Xu2e70b552018-03-02 16:22:10 -080097 } else if (name == "brotli_quality") {
98 if (!ParseQuality(optarg, &brotli_quality_, BROTLI_MIN_QUALITY,
99 BROTLI_MAX_QUALITY)) {
Tianjie Xu1f1cdb22017-11-20 11:05:55 -0800100 return false;
101 }
102 } else {
103 std::cerr << "Unrecognized options: " << name << endl;
104 return false;
105 }
106 }
107
108 // If quality is uninitialized for brotli, set it to default value.
Alex Deymoe790a3b2017-11-09 18:09:11 +0100109 if (format_ != BsdiffFormat::kLegacy &&
Tianjie Xu77833b62018-03-07 18:13:47 -0800110 IsCompressorSupported(CompressorType::kBrotli) && brotli_quality_ == -1) {
Tianjie Xu2e70b552018-03-02 16:22:10 -0800111 brotli_quality_ = kBrotliDefaultQuality;
Tianjie Xu77833b62018-03-07 18:13:47 -0800112 } else if (!IsCompressorSupported(CompressorType::kBrotli) &&
Tianjie Xu2e70b552018-03-02 16:22:10 -0800113 brotli_quality_ != -1) {
114 std::cerr << "Warning: Brotli quality is only used in the brotli"
115 " compressor.\n";
Tianjie Xu1f1cdb22017-11-20 11:05:55 -0800116 }
117
118 return true;
119}
120
Tianjie Xu77833b62018-03-07 18:13:47 -0800121bool BsdiffArguments::ParseCompressorTypes(const string& str,
122 std::set<CompressorType>* types) {
123 types->clear();
124 // The expected types string is separated by ":", e.g. bz2:brotli
125 std::vector<std::string> type_list;
126 size_t base = 0;
127 size_t found;
128 while (true) {
129 found = str.find(":", base);
130 type_list.emplace_back(str, base, found - base);
131
132 if (found == str.npos)
133 break;
134 base = found + 1;
Tianjie Xu1f1cdb22017-11-20 11:05:55 -0800135 }
Tianjie Xu77833b62018-03-07 18:13:47 -0800136
137 for (auto& type : type_list) {
138 std::transform(type.begin(), type.end(), type.begin(), ::tolower);
139 if (type == kNoCompressionString) {
140 types->emplace(CompressorType::kNoCompression);
141 } else if (type == kBZ2String) {
142 types->emplace(CompressorType::kBZ2);
143 } else if (type == kBrotliString) {
144 types->emplace(CompressorType::kBrotli);
145 } else {
146 std::cerr << "Failed to parse compressor type in " << str << endl;
147 return false;
148 }
149 }
150
151 return true;
Tianjie Xu1f1cdb22017-11-20 11:05:55 -0800152}
153
Alex Deymo383f6772018-02-08 15:50:11 +0100154bool BsdiffArguments::ParseMinLength(const string& str, size_t* len) {
155 errno = 0;
156 char* end;
157 const char* s = str.c_str();
158 long result = strtol(s, &end, 10);
159 if (errno != 0 || s == end || *end != '\0') {
160 return false;
161 }
162
163 if (result < 0) {
164 std::cerr << "Minimum length must be non-negative: " << str << endl;
165 return false;
166 }
167
168 *len = result;
169 return true;
170}
171
Tianjie Xu1f1cdb22017-11-20 11:05:55 -0800172bool BsdiffArguments::ParseBsdiffFormat(const string& str,
173 BsdiffFormat* format) {
174 string format_string = str;
175 std::transform(format_string.begin(), format_string.end(),
176 format_string.begin(), ::tolower);
177 if (format_string == kLegacyString || format_string == kBsdiff40String) {
178 *format = BsdiffFormat::kLegacy;
179 return true;
180 } else if (format_string == kBsdf2String) {
181 *format = BsdiffFormat::kBsdf2;
182 return true;
Alex Deymoe790a3b2017-11-09 18:09:11 +0100183 } else if (format_string == kEndsleyString) {
184 *format = BsdiffFormat::kEndsley;
185 return true;
Tianjie Xu1f1cdb22017-11-20 11:05:55 -0800186 }
187 std::cerr << "Failed to parse bsdiff format in " << str << endl;
188 return false;
189}
190
Tianjie Xu2e70b552018-03-02 16:22:10 -0800191bool BsdiffArguments::ParseQuality(const string& str,
192 int* quality,
193 int min,
194 int max) {
Tianjie Xu1f1cdb22017-11-20 11:05:55 -0800195 errno = 0;
196 char* end;
197 const char* s = str.c_str();
198 long result = strtol(s, &end, 10);
199 if (errno != 0 || s == end || *end != '\0') {
200 return false;
201 }
202
Tianjie Xu2e70b552018-03-02 16:22:10 -0800203 if (result < min || result > max) {
Tianjie Xu1f1cdb22017-11-20 11:05:55 -0800204 std::cerr << "Compression quality out of range " << str << endl;
205 return false;
206 }
207
208 *quality = result;
209 return true;
210}
211
Tianjie Xu77833b62018-03-07 18:13:47 -0800212bool BsdiffArguments::IsCompressorSupported(CompressorType type) const {
213 return compressor_types_.find(type) != compressor_types_.end();
214}
215
Tianjie Xu1f1cdb22017-11-20 11:05:55 -0800216} // namespace bsdiff