1 /*
2 * Copyright (c) 2023 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 "cl_option.h"
17 #include "cl_parser.h"
18
19 using namespace maplecl;
20
GetCommandLine()21 CommandLine &CommandLine::GetCommandLine()
22 {
23 static CommandLine cl;
24 return cl;
25 }
26
27 #ifdef ARK_LITECG_DEBUG
CheckJoinedOption(KeyArg & keyArg,OptionCategory & optCategory)28 OptionInterface *CommandLine::CheckJoinedOption(KeyArg &keyArg, OptionCategory &optCategory)
29 {
30 auto &str = keyArg.rawArg;
31
32 for (auto joinedOption : optCategory.joinedOptions) {
33 /* Joined Option (like -DMACRO) can be detected as substring (-D) in the option string */
34 if (str.find(joinedOption.first) == 0) {
35 size_t keySize = joinedOption.first.size();
36
37 keyArg.val = str.substr(keySize);
38 keyArg.key = str.substr(0, keySize);
39 keyArg.isJoinedOpt = true;
40 return joinedOption.second;
41 }
42 }
43
44 return nullptr;
45 }
46
ParseJoinedOption(size_t & argsIndex,const std::deque<std::string_view> & args,KeyArg & keyArg,OptionCategory & optCategory)47 RetCode CommandLine::ParseJoinedOption(size_t &argsIndex, const std::deque<std::string_view> &args, KeyArg &keyArg,
48 OptionCategory &optCategory)
49 {
50 OptionInterface *option = CheckJoinedOption(keyArg, optCategory);
51 if (option != nullptr) {
52 RetCode err = option->Parse(argsIndex, args, keyArg);
53 if (err != RetCode::noError) {
54 return err;
55 }
56
57 /* Set Option in all categories registering for this option */
58 for (auto &category : option->optCategories) {
59 category->AddEnabledOption(option);
60 }
61 } else {
62 return RetCode::notRegistered;
63 }
64
65 return RetCode::noError;
66 }
67
ParseOption(size_t & argsIndex,const std::deque<std::string_view> & args,KeyArg & keyArg,OptionCategory & optCategory,OptionInterface * opt)68 RetCode CommandLine::ParseOption(size_t &argsIndex, const std::deque<std::string_view> &args, KeyArg &keyArg,
69 OptionCategory &optCategory, OptionInterface *opt)
70 {
71 RetCode err = opt->Parse(argsIndex, args, keyArg);
72 if (err != RetCode::noError) {
73 return err;
74 }
75
76 /* Set Option in all categories registering for this option */
77 for (auto &category : opt->optCategories) {
78 category->AddEnabledOption(opt);
79 }
80
81 return RetCode::noError;
82 }
83
ParseEqualOption(size_t & argsIndex,const std::deque<std::string_view> & args,KeyArg & keyArg,OptionCategory & optCategory,const OptionsMapType & optMap,size_t pos)84 RetCode CommandLine::ParseEqualOption(size_t &argsIndex, const std::deque<std::string_view> &args, KeyArg &keyArg,
85 OptionCategory &optCategory, const OptionsMapType &optMap, size_t pos)
86 {
87 keyArg.isEqualOpt = true;
88 auto &arg = args[argsIndex];
89
90 /* To handle joined option, we must have full (not splitted key),
91 * because joined option splitting is different:
92 * As example for -Dkey=value: default splitting key="Dkey" value="value",
93 * Joined option splitting key="D" value="key=value"
94 */
95 auto item = optMap.find(std::string(arg.substr(0, pos)));
96 if (item != optMap.end()) {
97 /* equal option, like --key=value */
98 keyArg.key = arg.substr(0, pos);
99 keyArg.val = arg.substr(pos + 1);
100 return ParseOption(argsIndex, args, keyArg, optCategory, item->second);
101 } else {
102 /* It can be joined option, like: -DMACRO=VALUE */
103 return ParseJoinedOption(argsIndex, args, keyArg, optCategory);
104 }
105 }
106
ParseSimpleOption(size_t & argsIndex,const std::deque<std::string_view> & args,KeyArg & keyArg,OptionCategory & optCategory,const OptionsMapType & optMap)107 RetCode CommandLine::ParseSimpleOption(size_t &argsIndex, const std::deque<std::string_view> &args, KeyArg &keyArg,
108 OptionCategory &optCategory, const OptionsMapType &optMap)
109 {
110 keyArg.isEqualOpt = false;
111 auto &arg = args[argsIndex];
112
113 auto item = optMap.find(std::string(arg));
114 if (item != optMap.end()) {
115 /* --key or --key value */
116 return ParseOption(argsIndex, args, keyArg, optCategory, item->second);
117 } else {
118 /* It can be joined option, like: -DMACRO */
119 return ParseJoinedOption(argsIndex, args, keyArg, optCategory);
120 }
121 }
122 #endif
123
HandleInputArgs(const std::deque<std::string_view> & args,OptionCategory & optCategory)124 RetCode CommandLine::HandleInputArgs(const std::deque<std::string_view> &args, OptionCategory &optCategory)
125 {
126 RetCode err = RetCode::noError;
127
128 /* badCLArgs contains option parsing errors for each incorrect option.
129 * We should clear old badCLArgs results. */
130 badCLArgs.clear();
131
132 #ifdef ARK_LITECG_DEBUG
133 bool wasError = false;
134 #endif
135 for (size_t argsIndex = 0; argsIndex < args.size();) {
136 auto &arg = args[argsIndex];
137 if (arg == "") {
138 ++argsIndex;
139 continue;
140 }
141 #ifdef ARK_LITECG_DEBUG
142 KeyArg keyArg(arg);
143
144 auto pos = arg.find('=');
145 /* option like --key=value */
146 if (pos != std::string::npos) {
147 DEBUG_ASSERT(pos > 0, "CG internal error, composite unit with less than 2 unit elements.");
148 err = ParseEqualOption(argsIndex, args, keyArg, optCategory, optCategory.options, pos);
149 if (err != RetCode::noError) {
150 badCLArgs.emplace_back(args[argsIndex], err);
151 ++argsIndex;
152 wasError = true;
153 }
154 continue;
155 } else {
156 /* option like "--key value" or "--key" */
157 err = ParseSimpleOption(argsIndex, args, keyArg, optCategory, optCategory.options);
158 if (err != RetCode::noError) {
159 badCLArgs.emplace_back(args[argsIndex], err);
160 ++argsIndex;
161 wasError = true;
162 }
163 continue;
164 }
165 #endif
166 ++argsIndex;
167 continue;
168 }
169
170 #ifdef ARK_LITECG_DEBUG
171 if (wasError == true) {
172 return RetCode::parsingErr;
173 }
174 #endif
175
176 return err;
177 }
178
Parse(std::vector<std::string> argvs,OptionCategory & optCategory)179 RetCode CommandLine::Parse(std::vector<std::string> argvs, OptionCategory &optCategory)
180 {
181 std::deque<std::string_view> args;
182 for (size_t i = 0; i < argvs.size(); i++) {
183 args.emplace_back(argvs[i]);
184 }
185 return HandleInputArgs(args, optCategory);
186 }
187
Register(const std::vector<std::string> & optNames,OptionInterface & opt,OptionCategory & optCategory)188 void CommandLine::Register(const std::vector<std::string> &optNames, OptionInterface &opt, OptionCategory &optCategory)
189 {
190 for (auto &optName : optNames) {
191 if (optName.empty()) {
192 continue;
193 }
194
195 DEBUG_ASSERT(optCategory.options.count(optName) == 0, "Duplicated options name %s", optName.data());
196 optCategory.options.emplace(optName, &opt);
197
198 if (opt.IsJoinedValPermitted()) {
199 optCategory.joinedOptions.emplace(optName, &opt);
200 }
201 }
202
203 auto &disabledWith = opt.GetDisabledName();
204 if (!disabledWith.empty()) {
205 for (auto &disabledName : disabledWith) {
206 DEBUG_ASSERT(optCategory.options.count(disabledName) == 0, "Duplicated options name %s",
207 disabledName.data());
208 optCategory.options.emplace(disabledName, &opt);
209 }
210 }
211
212 optCategory.registredOptions.push_back(&opt);
213 opt.optCategories.push_back(&optCategory);
214 }