blob: ae23fe77e0571186cb2deee7e4a54d79b4b3e1d5 [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
44bool BsdiffArguments::IsValid() const {
Alex Deymoe790a3b2017-11-09 18:09:11 +010045 if (compressor_type_ == CompressorType::kBrotli &&
Tianjie Xu2e70b552018-03-02 16:22:10 -080046 (brotli_quality_ < BROTLI_MIN_QUALITY ||
47 brotli_quality_ > BROTLI_MAX_QUALITY)) {
Alex Deymoe790a3b2017-11-09 18:09:11 +010048 return false;
49 }
50
Tianjie Xu1f1cdb22017-11-20 11:05:55 -080051 if (format_ == BsdiffFormat::kLegacy) {
Alex Deymoe790a3b2017-11-09 18:09:11 +010052 return compressor_type_ == CompressorType::kBZ2;
Tianjie Xu1f1cdb22017-11-20 11:05:55 -080053 } else if (format_ == BsdiffFormat::kBsdf2) {
Alex Deymoe790a3b2017-11-09 18:09:11 +010054 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;
Tianjie Xu1f1cdb22017-11-20 11:05:55 -080059 }
60 return false;
61}
62
63bool 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
Alex Deymo383f6772018-02-08 15:50:11 +010071 string name = OPTIONS[option_index].name;
Tianjie Xu1f1cdb22017-11-20 11:05:55 -080072 if (name == "format") {
73 if (!ParseBsdiffFormat(optarg, &format_)) {
74 return false;
75 }
Alex Deymo383f6772018-02-08 15:50:11 +010076 } else if (name == "minlen") {
77 if (!ParseMinLength(optarg, &min_length_)) {
78 return false;
79 }
Tianjie Xu1f1cdb22017-11-20 11:05:55 -080080 } else if (name == "type") {
81 if (!ParseCompressorType(optarg, &compressor_type_)) {
82 return false;
83 }
Tianjie Xu2e70b552018-03-02 16:22:10 -080084 } else if (name == "brotli_quality") {
85 if (!ParseQuality(optarg, &brotli_quality_, BROTLI_MIN_QUALITY,
86 BROTLI_MAX_QUALITY)) {
Tianjie Xu1f1cdb22017-11-20 11:05:55 -080087 return false;
88 }
89 } else {
90 std::cerr << "Unrecognized options: " << name << endl;
91 return false;
92 }
93 }
94
95 // If quality is uninitialized for brotli, set it to default value.
Alex Deymoe790a3b2017-11-09 18:09:11 +010096 if (format_ != BsdiffFormat::kLegacy &&
Tianjie Xu2e70b552018-03-02 16:22:10 -080097 compressor_type_ == CompressorType::kBrotli && brotli_quality_ == -1) {
98 brotli_quality_ = kBrotliDefaultQuality;
Tianjie Xu1f1cdb22017-11-20 11:05:55 -080099 } else if (compressor_type_ != CompressorType::kBrotli &&
Tianjie Xu2e70b552018-03-02 16:22:10 -0800100 brotli_quality_ != -1) {
101 std::cerr << "Warning: Brotli quality is only used in the brotli"
102 " compressor.\n";
Tianjie Xu1f1cdb22017-11-20 11:05:55 -0800103 }
104
105 return true;
106}
107
108bool 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
Alex Deymo383f6772018-02-08 15:50:11 +0100127bool 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
Tianjie Xu1f1cdb22017-11-20 11:05:55 -0800145bool 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;
Alex Deymoe790a3b2017-11-09 18:09:11 +0100156 } else if (format_string == kEndsleyString) {
157 *format = BsdiffFormat::kEndsley;
158 return true;
Tianjie Xu1f1cdb22017-11-20 11:05:55 -0800159 }
160 std::cerr << "Failed to parse bsdiff format in " << str << endl;
161 return false;
162}
163
Tianjie Xu2e70b552018-03-02 16:22:10 -0800164bool BsdiffArguments::ParseQuality(const string& str,
165 int* quality,
166 int min,
167 int max) {
Tianjie Xu1f1cdb22017-11-20 11:05:55 -0800168 errno = 0;
169 char* end;
170 const char* s = str.c_str();
171 long result = strtol(s, &end, 10);
172 if (errno != 0 || s == end || *end != '\0') {
173 return false;
174 }
175
Tianjie Xu2e70b552018-03-02 16:22:10 -0800176 if (result < min || result > max) {
Tianjie Xu1f1cdb22017-11-20 11:05:55 -0800177 std::cerr << "Compression quality out of range " << str << endl;
178 return false;
179 }
180
181 *quality = result;
182 return true;
183}
184
185} // namespace bsdiff