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
18 using namespace maplecl;
19
20 /* ################################################################
21 * Utility Fuctions
22 * ################################################################ */
23
24 /* Anonymous namespace to restrict visibility of utility functions */
25 namespace {
26
27 #ifdef ARK_LITECG_DEBUG
IsPrefixDetected(std::string_view opt)28 bool IsPrefixDetected(std::string_view opt)
29 {
30 if (opt.substr(0, 2) == "--") { // whether opt start with 2 char "--"
31 return true;
32 }
33
34 /* It should be "--" or "-" */
35 return (opt[0] == '-');
36 }
37
38 /* NOTE: Returning size_t parameter is used to show how many command line arguments
39 * handled with this key. argsIndex must be incremented with this parameter outside of ExtractValue. */
ExtractValue(size_t argsIndex,const std::deque<std::string_view> & args,const OptionInterface & opt,KeyArg & keyArg)40 std::pair<RetCode, size_t> ExtractValue(size_t argsIndex, const std::deque<std::string_view> &args,
41 const OptionInterface &opt, KeyArg &keyArg)
42 {
43 /* The option like "--key= " does not contain a value after equal symbol */
44 if (keyArg.isEqualOpt == true && keyArg.val.empty()) {
45 return {RetCode::valueEmpty, 0};
46 }
47
48 bool canValBeSet = (keyArg.isEqualOpt || opt.IsJoinedValPermitted());
49 /* Value is parsed outside OptionType::Parse because it's the option
50 * like "--key=val" or joined option like -DValue */
51 if (canValBeSet && !keyArg.val.empty()) {
52 if (opt.ExpectedVal() == ValueExpectedType::kValueDisallowed) {
53 return {RetCode::unnecessaryValue, 0};
54 }
55 return {RetCode::noError, 0};
56 } else { // Need to parse second command line argument to check options value
57 /* Optional value can be set only with "=" like this --key=value */
58 if (opt.ExpectedVal() == ValueExpectedType::kValueDisallowed ||
59 opt.ExpectedVal() == ValueExpectedType::kValueOptional) {
60 return {RetCode::noError, 0};
61 }
62
63 /* localArgsIndex is used to be sure that nobody breaks the logic by
64 * changing argsIndex to reference. original argsIndex must be not changed here. */
65 size_t localArgsIndex = argsIndex + 1;
66 /* Second command line argument does not exist */
67 if (localArgsIndex >= args.size() || args[localArgsIndex].empty()) {
68 RetCode ret =
69 (opt.ExpectedVal() == ValueExpectedType::kValueRequired) ? RetCode::valueEmpty : RetCode::noError;
70 return {ret, 0};
71 }
72
73 keyArg.val = args[localArgsIndex];
74 return {RetCode::noError, 1};
75 }
76 }
77 #endif
78 } /* Anonymous namespace */
79
80 /* ################################################################
81 * Option Value Parsers Implementation
82 * ################################################################ */
83
84 template <>
ParseBool(size_t & argsIndex,const std::deque<std::string_view> & args)85 RetCode Option<bool>::ParseBool(size_t &argsIndex, const std::deque<std::string_view> &args)
86 {
87 #ifdef ARK_LITECG_DEBUG
88 /* DisabledName should set it to false, like --fno-omit-framepointer vs --fomit-framepointer */
89 auto disabledNames = GetDisabledName();
90 auto it = std::find(disabledNames.begin(), disabledNames.end(), args[argsIndex]);
91 SetValue(it == disabledNames.end());
92
93 ++argsIndex;
94 #endif
95 return RetCode::noError;
96 }
97
98 /* NOTE: argsIndex must be incremented only if option is handled successfully */
99 template <>
ParseString(size_t & argsIndex,const std::deque<std::string_view> & args,KeyArg & keyArg)100 RetCode Option<std::string>::ParseString(size_t &argsIndex, const std::deque<std::string_view> &args, KeyArg &keyArg)
101 {
102 #ifdef ARK_LITECG_DEBUG
103 RetCode err = RetCode::noError;
104 size_t indexIncCnt = 0;
105 std::tie(err, indexIncCnt) = ExtractValue(argsIndex, args, *this, keyArg);
106 if (err != RetCode::noError) {
107 return err;
108 }
109
110 if (keyArg.val.empty()) {
111 if (ExpectedVal() == ValueExpectedType::kValueRequired) {
112 return RetCode::valueEmpty;
113 }
114 ++argsIndex;
115 return RetCode::noError;
116 }
117
118 /* Value is not empty but ValueDisallowed is set */
119 if (ExpectedVal() == ValueExpectedType::kValueDisallowed) {
120 return RetCode::unnecessaryValue;
121 }
122
123 if (!keyArg.isEqualOpt && IsPrefixDetected(keyArg.val)) {
124 if (ExpectedVal() == ValueExpectedType::kValueRequired) {
125 return RetCode::valueEmpty;
126 }
127 ++argsIndex;
128 return RetCode::noError;
129 }
130
131 SetValue(std::string(keyArg.val));
132 if (keyArg.isEqualOpt && !keyArg.isJoinedOpt) {
133 /* isJoinedOpt is used to prevent -DMACRO=VALUE.
134 * -DMACRO=VALUE uses "=" sign but it's not the separator between Option and Value,
135 * In this case it's a part of Value.
136 */
137 SetEqualType(EqualType::kWithEqual);
138 }
139
140 argsIndex += 1 + indexIncCnt; // 1 for key, indexIncCnt for Key Value from ExtractValue
141 #endif
142 return RetCode::noError;
143 }
144
145 /* NOTE: argsIndex must be incremented only if option is handled successfully */
146 template <typename T>
ParseDigit(size_t & argsIndex,const std::deque<std::string_view> & args,KeyArg & keyArg)147 RetCode Option<T>::ParseDigit(size_t &argsIndex, const std::deque<std::string_view> &args, KeyArg &keyArg)
148 {
149 #ifdef ARK_LITECG_DEBUG
150 static_assert(digitalCheck<T>, "Expected (u)intXX types");
151
152 RetCode err = RetCode::noError;
153 size_t indexIncCnt = 0;
154 std::tie(err, indexIncCnt) = ExtractValue(argsIndex, args, *this, keyArg);
155 if (err != RetCode::noError) {
156 return err;
157 }
158
159 if (keyArg.val.empty()) {
160 if (ExpectedVal() == ValueExpectedType::kValueRequired) {
161 return RetCode::valueEmpty;
162 }
163 ++argsIndex;
164 return RetCode::noError;
165 }
166
167 /* Value is not empty but ValueDisallowed is set */
168 if (ExpectedVal() == ValueExpectedType::kValueDisallowed) {
169 return RetCode::unnecessaryValue;
170 }
171
172 T resDig = 0;
173 uint64_t udig = 0;
174 int64_t dig = 0;
175 char *endStrPtr;
176 errno = 0;
177 if constexpr (std::is_same_v<uint64_t, T> || std::is_same_v<uint32_t, T> || std::is_same_v<uint16_t, T> ||
178 std::is_same_v<uint8_t, T>) {
179 if (keyArg.val.data()[0] == '-') {
180 return RetCode::incorrectValue;
181 }
182
183 udig = std::strtoull(keyArg.val.data(), &endStrPtr, 0);
184 resDig = static_cast<T>(udig);
185 } else {
186 dig = std::strtoll(keyArg.val.data(), &endStrPtr, 0);
187 resDig = static_cast<T>(dig);
188 }
189
190 bool u32Type = std::is_same<T, uint32_t>::value;
191 bool i32Type = std::is_same<T, int32_t>::value;
192 bool u16Type = std::is_same<T, uint16_t>::value;
193 bool u8Type = std::is_same<T, uint8_t>::value;
194 bool i16Type = std::is_same<T, int16_t>::value;
195 bool i8Type = std::is_same<T, int8_t>::value;
196
197 if (*endStrPtr != '\0') {
198 return RetCode::incorrectValue;
199 }
200
201 if (errno != 0) {
202 return RetCode::outOfRange;
203 }
204
205 if (u32Type && udig > UINT32_MAX) {
206 return RetCode::outOfRange;
207 }
208
209 if (i32Type && (dig > INT32_MAX || dig < INT32_MIN)) {
210 return RetCode::outOfRange;
211 }
212
213 if (u16Type && udig > UINT16_MAX) {
214 return RetCode::outOfRange;
215 }
216
217 if (i16Type && (dig > INT16_MAX || dig < INT16_MIN)) {
218 return RetCode::outOfRange;
219 }
220
221 if (u8Type && udig > UINT8_MAX) {
222 return RetCode::outOfRange;
223 }
224
225 if (i8Type && (dig > INT8_MAX || dig < INT8_MIN)) {
226 return RetCode::outOfRange;
227 }
228
229 SetValue(resDig);
230 if (keyArg.isEqualOpt && !keyArg.isJoinedOpt) {
231 /* isJoinedOpt is used to prevent -DMACRO=VALUE.
232 * -DMACRO=VALUE uses "=" sign but it's not the separator between Option and Value,
233 * In this case it's a part of Value.
234 */
235 SetEqualType(EqualType::kWithEqual);
236 }
237
238 argsIndex += 1 + indexIncCnt; // 1 for key, indexIncCnt for Key Value from ExtractValue
239 #endif
240 return RetCode::noError;
241 }
242
243 /* Needed to describe OptionType<>::Parse template in this .cpp file */
244 template class maplecl::Option<uint8_t>;
245 template class maplecl::Option<uint16_t>;
246 template class maplecl::Option<uint32_t>;
247 template class maplecl::Option<uint64_t>;
248 template class maplecl::Option<int8_t>;
249 template class maplecl::Option<int16_t>;
250 template class maplecl::Option<int32_t>;
251 template class maplecl::Option<int64_t>;
252