1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <option_parser.h>
17
18 #include <iomanip>
19 #include <sstream>
20
21 enum {
22 PARSER_NEXT = 0,
23 PARSER_ERROR = 1,
24 PARSER_PARSED = 2,
25 PARSER_PARSED_MORE = 3,
26 };
27
ParseArgument(const char * arg,const char * arg2)28 int32_t OptionParser::ParseArgument(const char *arg, const char *arg2)
29 {
30 if (arguments.empty()) {
31 return PARSER_NEXT;
32 }
33
34 std::stringstream ss(arg);
35 switch (arguments.front().type) {
36 case Argument::ValueType::i32:
37 ss >> std::setbase(0) >> arguments.front().result->i32;
38 break;
39 case Argument::ValueType::u32:
40 ss >> std::setbase(0) >> arguments.front().result->u32;
41 break;
42 case Argument::ValueType::i64:
43 ss >> std::setbase(0) >> arguments.front().result->i64;
44 break;
45 case Argument::ValueType::f64:
46 ss >> arguments.front().result->f64;
47 break;
48 case Argument::ValueType::str:
49 ss >> arguments.front().result->str;
50 break;
51 }
52
53 if (!ss.eof() || !ss) {
54 error = "parse ";
55 error = error + arg + " error";
56 return PARSER_ERROR;
57 }
58
59 arguments.pop_front();
60 return PARSER_PARSED;
61 }
62
ParseArgc(const char * arg,const char * arg2)63 int32_t OptionParser::ParseArgc(const char *arg, const char *arg2)
64 {
65 if (arg[0] == 0) {
66 return PARSER_ERROR;
67 }
68
69 if (arg[0] != '-') {
70 skipped.push_back(arg);
71 return PARSER_PARSED;
72 }
73
74 if (arg[1] == 0) {
75 return PARSER_ERROR;
76 }
77 return PARSER_NEXT;
78 }
79
ParseShortOption(const char * arg1,const char * arg2)80 int32_t OptionParser::ParseShortOption(const char *arg1, const char *arg2)
81 {
82 if (arg1[1] == '-') {
83 // long option
84 return PARSER_NEXT;
85 }
86
87 for (const auto &option : options) {
88 if (option.so == &arg1[1]) {
89 if (option.type == Option::ValueType::bol) {
90 option.result->bl = !option.result->bl;
91 return PARSER_PARSED;
92 } else if (arg2 == nullptr) {
93 error = option.so + " need argument";
94 return PARSER_ERROR;
95 }
96
97 std::stringstream ss(arg2);
98 switch (option.type) {
99 case Option::ValueType::i32:
100 ss >> std::setbase(0) >> option.result->i32;
101 break;
102 case Option::ValueType::u32:
103 ss >> std::setbase(0) >> option.result->u32;
104 break;
105 case Option::ValueType::i64:
106 ss >> std::setbase(0) >> option.result->i64;
107 break;
108 case Option::ValueType::f64:
109 ss >> option.result->f64;
110 break;
111 case Option::ValueType::str:
112 ss >> option.result->str;
113 break;
114 default:
115 break;
116 }
117
118 if (!ss.eof() || !ss) {
119 error = "parse ";
120 error = error + arg1 + " error, " + arg2;
121 return PARSER_ERROR;
122 }
123
124 return PARSER_PARSED_MORE;
125 }
126 }
127 return PARSER_NEXT;
128 }
129
ParseLongEqualOption(const char * arg,const char * arg2)130 int32_t OptionParser::ParseLongEqualOption(const char *arg, const char *arg2)
131 {
132 if (arg[1] != '-') {
133 return PARSER_NEXT;
134 }
135
136 int32_t ret = 0;
137 bool parsed = false;
138 for (const char *c = arg; *c; c++) {
139 if (*c == '=') {
140 std::string arg1(arg, c - arg);
141 std::string arg2(c + 1);
142 ret = ParseLongOption(arg1.c_str(), arg2.c_str());
143 parsed = true;
144 break;
145 }
146 }
147
148 if (ret == PARSER_ERROR || ret == PARSER_NEXT) {
149 return ret;
150 }
151
152 if (parsed) {
153 return PARSER_PARSED;
154 }
155 return PARSER_NEXT;
156 }
157
ParseLongOption(const char * arg1,const char * arg2)158 int32_t OptionParser::ParseLongOption(const char *arg1, const char *arg2)
159 {
160 if (arg1[1] != '-') {
161 return PARSER_NEXT;
162 }
163
164 for (const auto &option : options) {
165 if (option.lo == &arg1[0x2]) {
166 if (option.type == Option::ValueType::bol) {
167 option.result->bl = !option.result->bl;
168 return PARSER_PARSED;
169 } else if (arg2 == nullptr) {
170 error = option.lo + " need argument";
171 return PARSER_ERROR;
172 }
173
174 std::stringstream ss(arg2);
175 switch (option.type) {
176 case Option::ValueType::i32:
177 ss >> std::setbase(0) >> option.result->i32;
178 break;
179 case Option::ValueType::u32:
180 ss >> std::setbase(0) >> option.result->u32;
181 break;
182 case Option::ValueType::i64:
183 ss >> std::setbase(0) >> option.result->i64;
184 break;
185 case Option::ValueType::f64:
186 ss >> option.result->f64;
187 break;
188 case Option::ValueType::str:
189 ss >> option.result->str;
190 break;
191 default:
192 break;
193 }
194
195 if (!ss.eof() || !ss) {
196 error = "parse ";
197 error = error + arg1 + " error, " + arg2;
198 return PARSER_ERROR;
199 }
200 return PARSER_PARSED_MORE;
201 }
202 }
203 return PARSER_NEXT;
204 }
205
AddSkipped(const char * arg,const char * arg2)206 int32_t OptionParser::AddSkipped(const char *arg, const char *arg2)
207 {
208 skipped.push_back(arg);
209 return PARSER_PARSED;
210 }
211
Parse(int32_t argc,const char ** argv)212 int32_t OptionParser::Parse(int32_t argc, const char **argv)
213 {
214 int32_t (OptionParser:: *parsers[])(const char *, const char *) = {
215 &OptionParser::ParseArgument,
216 &OptionParser::ParseArgc,
217 &OptionParser::ParseShortOption,
218 &OptionParser::ParseLongEqualOption,
219 &OptionParser::ParseLongOption,
220 &OptionParser::AddSkipped,
221 };
222 for (int32_t i = 0; i < argc; i++) {
223 for (auto &parser : parsers) {
224 auto ret = (this->*parser)(argv[i], argv[i + 1]);
225 if (ret == PARSER_ERROR) {
226 return ret;
227 } else if (ret == PARSER_PARSED_MORE) {
228 i++;
229 break;
230 } else if (ret == PARSER_PARSED) {
231 break;
232 }
233 }
234 }
235
236 if (!arguments.empty()) {
237 error = "need more arguments";
238 return 1;
239 } else {
240 skipped.push_back(nullptr);
241 }
242
243 return 0;
244 }
245
AddOption(const std::string & shortOpt,const std::string & longOpt,void * result,Option::ValueType type)246 int32_t OptionParser::AddOption(const std::string &shortOpt,
247 const std::string &longOpt, void *result, Option::ValueType type)
248 {
249 struct Option option = {
250 .so = shortOpt,
251 .lo = longOpt,
252 .result = reinterpret_cast<union Option::Value *>(result),
253 .type = type,
254 };
255 options.emplace_back(std::move(option));
256 return 0;
257 }
258
AddArguments(void * result,Argument::ValueType type)259 int32_t OptionParser::AddArguments(void *result, Argument::ValueType type)
260 {
261 struct Argument argument = {
262 .result = reinterpret_cast<union Argument::Value *>(result),
263 .type = type,
264 };
265 arguments.emplace_back(std::move(argument));
266 return 0;
267 }
268
GetErrorString()269 std::string OptionParser::GetErrorString()
270 {
271 return error;
272 }
273
GetSkippedArgc()274 int32_t OptionParser::GetSkippedArgc()
275 {
276 return skipped.size() - 1;
277 }
278
GetSkippedArgv()279 const char **OptionParser::GetSkippedArgv()
280 {
281 return skipped.data();
282 }
283