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