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