blob: d6ca2bef94ddf401ee940c7a1e5ca3d5579a91de [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";
28
29const struct option OPTIONS[] = {
30 {"format", required_argument, nullptr, 0},
Alex Deymo383f6772018-02-08 15:50:11 +010031 {"minlen", required_argument, nullptr, 0},
Tianjie Xu1f1cdb22017-11-20 11:05:55 -080032 {"type", required_argument, nullptr, 0},
33 {"quality", required_argument, nullptr, 0},
34 {nullptr, 0, nullptr, 0},
35};
36
37const uint32_t kBrotliDefaultQuality = BROTLI_MAX_QUALITY;
38
39} // namespace
40
41namespace bsdiff {
42
43bool BsdiffArguments::IsValid() const {
44 if (format_ == BsdiffFormat::kLegacy) {
45 return (compressor_type_ == CompressorType::kBZ2);
46 } else if (format_ == BsdiffFormat::kBsdf2) {
47 if (compressor_type_ == CompressorType::kBZ2) {
48 return true;
49 }
50 if (compressor_type_ == CompressorType::kBrotli) {
51 return (compression_quality_ >= BROTLI_MIN_QUALITY &&
52 compression_quality_ <= BROTLI_MAX_QUALITY);
53 }
54 }
55 return false;
56}
57
58bool BsdiffArguments::ParseCommandLine(int argc, char** argv) {
59 int opt;
60 int option_index;
61 while ((opt = getopt_long(argc, argv, "", OPTIONS, &option_index)) != -1) {
62 if (opt != 0) {
63 return false;
64 }
65
Alex Deymo383f6772018-02-08 15:50:11 +010066 string name = OPTIONS[option_index].name;
Tianjie Xu1f1cdb22017-11-20 11:05:55 -080067 if (name == "format") {
68 if (!ParseBsdiffFormat(optarg, &format_)) {
69 return false;
70 }
Alex Deymo383f6772018-02-08 15:50:11 +010071 } else if (name == "minlen") {
72 if (!ParseMinLength(optarg, &min_length_)) {
73 return false;
74 }
Tianjie Xu1f1cdb22017-11-20 11:05:55 -080075 } else if (name == "type") {
76 if (!ParseCompressorType(optarg, &compressor_type_)) {
77 return false;
78 }
79 } else if (name == "quality") {
80 if (!ParseQuality(optarg, &compression_quality_)) {
81 return false;
82 }
83 } else {
84 std::cerr << "Unrecognized options: " << name << endl;
85 return false;
86 }
87 }
88
89 // If quality is uninitialized for brotli, set it to default value.
90 if (format_ == BsdiffFormat::kBsdf2 &&
91 compressor_type_ == CompressorType::kBrotli &&
92 compression_quality_ == -1) {
93 compression_quality_ = kBrotliDefaultQuality;
94 } else if (compressor_type_ != CompressorType::kBrotli &&
95 compression_quality_ != -1) {
96 std::cerr << "Warning: Compression quality is only used in the brotli"
97 " compressor." << endl;
98 }
99
100 return true;
101}
102
103bool BsdiffArguments::ParseCompressorType(const string& str,
104 CompressorType* type) {
105 string type_string = str;
106 std::transform(type_string.begin(), type_string.end(), type_string.begin(),
107 ::tolower);
108 if (type_string == kNoCompressionString) {
109 *type = CompressorType::kNoCompression;
110 return true;
111 } else if (type_string == kBZ2String) {
112 *type = CompressorType::kBZ2;
113 return true;
114 } else if (type_string == kBrotliString) {
115 *type = CompressorType::kBrotli;
116 return true;
117 }
118 std::cerr << "Failed to parse compressor type in " << str << endl;
119 return false;
120}
121
Alex Deymo383f6772018-02-08 15:50:11 +0100122bool BsdiffArguments::ParseMinLength(const string& str, size_t* len) {
123 errno = 0;
124 char* end;
125 const char* s = str.c_str();
126 long result = strtol(s, &end, 10);
127 if (errno != 0 || s == end || *end != '\0') {
128 return false;
129 }
130
131 if (result < 0) {
132 std::cerr << "Minimum length must be non-negative: " << str << endl;
133 return false;
134 }
135
136 *len = result;
137 return true;
138}
139
Tianjie Xu1f1cdb22017-11-20 11:05:55 -0800140bool BsdiffArguments::ParseBsdiffFormat(const string& str,
141 BsdiffFormat* format) {
142 string format_string = str;
143 std::transform(format_string.begin(), format_string.end(),
144 format_string.begin(), ::tolower);
145 if (format_string == kLegacyString || format_string == kBsdiff40String) {
146 *format = BsdiffFormat::kLegacy;
147 return true;
148 } else if (format_string == kBsdf2String) {
149 *format = BsdiffFormat::kBsdf2;
150 return true;
151 }
152 std::cerr << "Failed to parse bsdiff format in " << str << endl;
153 return false;
154}
155
156bool BsdiffArguments::ParseQuality(const string& str, int* quality) {
157 errno = 0;
158 char* end;
159 const char* s = str.c_str();
160 long result = strtol(s, &end, 10);
161 if (errno != 0 || s == end || *end != '\0') {
162 return false;
163 }
164
165 if (result < BROTLI_MIN_QUALITY || result > BROTLI_MAX_QUALITY) {
166 std::cerr << "Compression quality out of range " << str << endl;
167 return false;
168 }
169
170 *quality = result;
171 return true;
172}
173
174} // namespace bsdiff