1 /**
2  * Copyright (c) 2021-2024 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 #ifndef LIBPANDABASE_UTILS_PANDARGS_H
17 #define LIBPANDABASE_UTILS_PANDARGS_H
18 
19 #include <algorithm>
20 #include <array>
21 #include <list>
22 #include <set>
23 #include <map>
24 #include <string>
25 #include <string_view>
26 #include <sstream>
27 #include <type_traits>
28 #include <vector>
29 #include <cerrno>
30 #include <optional>
31 #include <utility>
32 
33 #include "macros.h"
34 
35 namespace panda {
36 class PandArgBase;
37 using sub_args_t = std::vector<PandArgBase *>;
38 using arg_list_t = std::vector<std::string>;
39 using std::enable_if_t;
40 using std::is_same_v;
41 
42 enum class PandArgType : uint8_t { STRING, INTEGER, DOUBLE, BOOL, LIST, UINT32, UINT64, COMPOUND, NOTYPE };
43 
44 // Base class for panda argument
45 class PandArgBase {
46 public:
PandArgBase(std::string name, std::string desc, PandArgType type = PandArgType::NOTYPE)47     explicit PandArgBase(std::string name, std::string desc, PandArgType type = PandArgType::NOTYPE)
48         : name_(std::move(name)), desc_(std::move(desc)), type_(type)
49     {
50     }
51 
GetType() const52     PandArgType GetType() const
53     {
54         return type_;
55     }
56 
GetName() const57     std::string GetName() const
58     {
59         return name_;
60     }
61 
GetDesc() const62     std::string GetDesc() const
63     {
64         return desc_;
65     }
66 
WasSet() const67     bool WasSet() const
68     {
69         return was_set_;
70     }
71 
SetWasSet(bool value)72     void SetWasSet(bool value)
73     {
74         was_set_ = value;
75     }
76 
77     virtual void ResetDefaultValue() = 0;
78 
79 private:
80     std::string name_;
81     std::string desc_;
82     PandArgType type_;
83     bool was_set_ {false};
84 };
85 
86 template <typename T,
87           enable_if_t<is_same_v<std::string, T> || is_same_v<double, T> || is_same_v<bool, T> || is_same_v<int, T> ||
88                       is_same_v<uint32_t, T> || is_same_v<uint64_t, T> || is_same_v<arg_list_t, T>> * = nullptr>
89 class PandArg : public PandArgBase {
90 public:
PandArg(const std::string &name, T default_val, const std::string &desc)91     explicit PandArg(const std::string &name, T default_val, const std::string &desc)
92         : PandArgBase(name, desc, this->EvalType()), default_val_(default_val), real_val_(default_val)
93     {
94     }
95 
PandArg(const std::string &name, T default_val, const std::string &desc, PandArgType type)96     explicit PandArg(const std::string &name, T default_val, const std::string &desc, PandArgType type)
97         : PandArgBase(name, desc, type), default_val_(default_val), real_val_(default_val)
98     {
99     }
100 
PandArg(const std::string &name, int default_val, const std::string &desc, T min_val, T max_val)101     explicit PandArg(const std::string &name, int default_val, const std::string &desc, T min_val, T max_val)
102         : PandArgBase(name, desc, this->EvalType()),
103           default_val_(default_val),
104           real_val_(default_val),
105           min_max_val_(std::pair<T, T>(min_val, max_val))
106     {
107     }
108 
PandArg(const std::string &name, const arg_list_t &default_val, const std::string &desc, std::string delimiter)109     explicit PandArg(const std::string &name, const arg_list_t &default_val, const std::string &desc,
110                      std::string delimiter)
111         : PandArgBase(name, desc, PandArgType::LIST),
112           default_val_(default_val),
113           real_val_(default_val),
114           delimiter_(std::move(delimiter))
115     {
116     }
117 
GetValue() const118     T GetValue() const
119     {
120         return real_val_;
121     }
122 
GetDefaultValue() const123     T GetDefaultValue() const
124     {
125         return default_val_;
126     }
127 
128     template <bool update_flag = true>
SetValue(T val)129     void SetValue(T val)
130     {
131         real_val_ = val;
132         // NOLINTNEXTLINE(bugprone-suspicious-semicolon)
133         if constexpr (update_flag) {  // NOLINT(readability-braces-around-statements)
134             SetWasSet(true);
135         }
136     }
137 
138     void ResetDefaultValue() override
139     {
140         real_val_ = default_val_;
141     }
142 
GetDelimiter() const143     std::optional<std::string> GetDelimiter() const
144     {
145         return delimiter_;
146     }
GetMinMaxVal()147     std::optional<std::pair<T, T>> GetMinMaxVal()
148     {
149         return min_max_val_;
150     }
151 
152 private:
EvalType()153     constexpr PandArgType EvalType()
154     {
155         // NOLINTNEXTLINE(bugprone-branch-clone)
156         if constexpr (is_same_v<std::string, T>) {  // NOLINT(readability-braces-around-statements)
157             return PandArgType::STRING;
158             // NOLINTNEXTLINE(readability-braces-around-statements,readability-misleading-indentation)
159         } else if constexpr (is_same_v<double, T>) {
160             return PandArgType::DOUBLE;
161             // NOLINTNEXTLINE(readability-braces-around-statements,readability-misleading-indentation)
162         } else if constexpr (is_same_v<bool, T>) {
163             return PandArgType::BOOL;
164             // NOLINTNEXTLINE(readability-braces-around-statements,readability-misleading-indentation)
165         } else if constexpr (is_same_v<int, T>) {
166             return PandArgType::INTEGER;
167             // NOLINTNEXTLINE(readability-braces-around-statements,readability-misleading-indentation)
168         } else if constexpr (is_same_v<uint32_t, T>) {
169             return PandArgType::UINT32;
170             // NOLINTNEXTLINE(readability-braces-around-statements,readability-misleading-indentation)
171         } else if constexpr (is_same_v<uint64_t, T>) {
172             return PandArgType::UINT64;
173             // NOLINTNEXTLINE(readability-braces-around-statements,readability-misleading-indentation)
174         } else if constexpr (is_same_v<arg_list_t, T>) {
175             return PandArgType::LIST;
176         }
177         UNREACHABLE();
178     }
179 
180     T default_val_;
181     T real_val_;
182 
183     // Only for integer arguments with range
184     std::optional<std::pair<T, T>> min_max_val_;
185 
186     // Only for strings with delimiter
187     std::optional<std::string> delimiter_;
188 };
189 
190 class PandArgCompound : public PandArg<bool> {
191 public:
PandArgCompound(const std::string &name, const std::string &desc, std::initializer_list<PandArgBase *> sub_args)192     PandArgCompound(const std::string &name, const std::string &desc, std::initializer_list<PandArgBase *> sub_args)
193         : PandArg<bool>(name, false, desc, PandArgType::COMPOUND), sub_args_(sub_args)
194     {
195     }
196 
FindSubArg(std::string_view name)197     PandArgBase *FindSubArg(std::string_view name)
198     {
199         auto res = std::find_if(sub_args_.begin(), sub_args_.end(),
200                                 [name](const auto &arg) { return arg->GetName() == name; });
201         return res == sub_args_.end() ? nullptr : *res;
202     }
203 
204     void ResetDefaultValue() override
205     {
206         PandArg<bool>::ResetDefaultValue();
207         std::for_each(sub_args_.begin(), sub_args_.end(), [](auto &arg) { arg->ResetDefaultValue(); });
208     }
209 
GetSubArgs() const210     const auto &GetSubArgs() const
211     {
212         return sub_args_;
213     }
214 
215 private:
216     sub_args_t sub_args_;
217 };
218 
219 class PandArgParser {
220 public:
Add(PandArgBase *arg)221     bool Add(PandArgBase *arg)
222     {
223         if (arg == nullptr) {
224             errstr_ += "pandargs: Can't add `nullptr` as an argument\n";
225             return false;
226         }
227         bool success = args_.insert(arg).second;
228         if (!success) {
229             errstr_ += "pandargs: Argument " + arg->GetName() + " has duplicate\n";
230         }
231         return success;
232     }
233 
PushBackTail(PandArgBase *arg)234     bool PushBackTail(PandArgBase *arg)
235     {
236         if (arg == nullptr) {
237             errstr_ += "pandargs: Can't add `nullptr` as a tail argument\n";
238             return false;
239         }
240         if (std::find(tail_args_.begin(), tail_args_.end(), arg) != tail_args_.end()) {
241             errstr_ += "pandargs: Tail argument " + arg->GetName() + " is already in tail arguments list\n";
242             return false;
243         }
244         tail_args_.emplace_back(arg);
245         return true;
246     }
247 
PopBackTail()248     bool PopBackTail()
249     {
250         if (tail_args_.empty()) {
251             errstr_ += "pandargs: Nothing to pop back from tail arguments\n";
252             return false;
253         }
254         tail_args_.pop_back();
255         return true;
256     }
257 
EraseTail()258     void EraseTail()
259     {
260         tail_args_.erase(tail_args_.begin(), tail_args_.end());
261     }
262 
Parse(const std::vector<std::string> &argv_vec)263     bool Parse(const std::vector<std::string> &argv_vec)
264     {
265         InitDefault();
266         std::copy(argv_vec.begin(), argv_vec.end(), std::back_inserter(argv_vec_));
267         return ParseArgs();
268     }
269 
Parse(int argc, const char *const argv[])270     bool Parse(int argc, const char *const argv[])  // NOLINT(modernize-avoid-c-arrays, hicpp-avoid-c-arrays)
271     {
272         InitDefault();
273         for (int i = 1; i < argc; i++) {
274             argv_vec_.emplace_back(argv[i]);  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
275         }
276         return ParseArgs();
277     }
278 
279     /**
280      * Parses a string to the option's value.
281      */
ParseSingleArg(PandArgBase *option, const std::string_view &option_value)282     bool ParseSingleArg(PandArgBase *option, const std::string_view &option_value)
283     {
284         ASSERT(option != nullptr);
285         argv_vec_ = {std::string(option_value)};
286         argv_index_ = 0;
287         argv_index_ += ParseNextParam(option, argv_vec_[argv_index_]);
288         return errstr_.empty();
289     }
290 
291     /**
292      * Parses option's name and returns corresponding pointer.
293      */
GetPandArg(const std::string_view &arg_name)294     PandArgBase *GetPandArg(const std::string_view &arg_name)
295     {
296         auto arg_it = args_.find(arg_name);
297         return (arg_it != args_.end()) ? *arg_it : nullptr;
298     }
299 
GetErrorString() const300     std::string GetErrorString() const
301     {
302         return errstr_;
303     }
304 
EnableTail()305     void EnableTail()
306     {
307         tail_flag_ = true;
308     }
309 
DisableTail()310     void DisableTail()
311     {
312         tail_flag_ = false;
313     }
314 
IsTailEnabled() const315     bool IsTailEnabled() const
316     {
317         return tail_flag_;
318     }
319 
GetTailSize() const320     std::size_t GetTailSize() const
321     {
322         return tail_args_.size();
323     }
324 
325     void EnableRemainder() noexcept
326     {
327         remainder_flag_ = true;
328     }
329 
330     void DisableRemainder() noexcept
331     {
332         remainder_flag_ = false;
333     }
334 
IsRemainderEnabled() const335     bool IsRemainderEnabled() const
336     {
337         return remainder_flag_;
338     }
339 
GetRemainder()340     arg_list_t GetRemainder()
341     {
342         return remainder_;
343     }
344 
IsArgSet(PandArgBase *arg) const345     bool IsArgSet(PandArgBase *arg) const
346     {
347         return args_.find(arg) != args_.end();
348     }
349 
IsArgSet(const std::string &arg_name) const350     bool IsArgSet(const std::string &arg_name) const
351     {
352         return args_.find(arg_name) != args_.end();
353     }
354 
GetHelpString() const355     std::string GetHelpString() const
356     {
357         std::ostringstream helpstr;
358         for (auto i : args_) {
359             if (i->GetType() == PandArgType::COMPOUND) {
360                 auto arg = static_cast<PandArgCompound *>(i);
361                 helpstr << DOUBLE_DASH << i->GetName() << ": " << i->GetDesc() << "\n";
362                 helpstr << "  Sub arguments:\n";
363                 for (auto sub_arg : arg->GetSubArgs()) {
364                     helpstr << "    " << sub_arg->GetName() << ": " << sub_arg->GetDesc() << "\n";
365                 }
366             } else {
367                 helpstr << DOUBLE_DASH << i->GetName() << ": " << i->GetDesc() << "\n";
368             }
369         }
370         if (!tail_args_.empty()) {
371             helpstr << "Tail arguments:\n";
372             for (auto i : tail_args_) {
373                 helpstr << i->GetName() << ": " << i->GetDesc() << "\n";
374             }
375         }
376         return helpstr.str();
377     }
378 
GetRegularArgs()379     std::string GetRegularArgs()
380     {
381         std::string args_str;
382         std::string value;
383         for (auto i : args_) {
384             switch (i->GetType()) {
385                 case PandArgType::STRING:
386                     value = static_cast<PandArg<std::string> *>(i)->GetValue();
387                     break;
388                 case PandArgType::INTEGER:
389                     value = std::to_string(static_cast<PandArg<int> *>(i)->GetValue());
390                     break;
391                 case PandArgType::DOUBLE:
392                     value = std::to_string(static_cast<PandArg<double> *>(i)->GetValue());
393                     break;
394                 case PandArgType::BOOL:
395                     // NOLINTNEXTLINE(readability-implicit-bool-conversion)
396                     value = std::to_string(static_cast<PandArg<bool> *>(i)->GetValue());
397                     break;
398                 case PandArgType::UINT32:
399                     value = std::to_string(static_cast<PandArg<uint32_t> *>(i)->GetValue());
400                     break;
401                 case PandArgType::UINT64:
402                     value = std::to_string(static_cast<PandArg<uint64_t> *>(i)->GetValue());
403                     break;
404                 case PandArgType::LIST: {
405                     value = "";
406                     std::vector<std::string> values_buf = static_cast<PandArg<arg_list_t> *>(i)->GetValue();
407                     for (const auto &j : values_buf) {
408                         value += j + ", ";
409                     }
410                     break;
411                 }
412                 default:
413                     errstr_ += "Invalid argument type \"" + i->GetName() + "\"\n";
414                     break;
415             }
416             args_str += DOUBLE_DASH + i->GetName() + "=" + value + "\n";
417         }
418         return args_str;
419     }
420 
421 private:
422     struct PandArgPtrComparator {
423         using is_transparent = void;
operator ()panda::PandArgParser::PandArgPtrComparator424         bool operator()(const PandArgBase *lhs, const PandArgBase *rhs) const
425         {
426             return lhs->GetName() < rhs->GetName();
427         }
operator ()panda::PandArgParser::PandArgPtrComparator428         bool operator()(std::string_view lhs, const PandArgBase *rhs) const
429         {
430             return lhs < rhs->GetName();
431         }
operator ()panda::PandArgParser::PandArgPtrComparator432         bool operator()(const PandArgBase *lhs, std::string_view rhs) const
433         {
434             return lhs->GetName() < rhs;
435         }
436     };
437 
ParseArgs()438     bool ParseArgs()
439     {
440         while (argv_index_ < argv_vec_.size()) {
441             PandArgBase *parsed_arg = ParseNextArg();
442             if (!errstr_.empty()) {
443                 return false;
444             }
445             if (argv_index_ < argv_vec_.size()) {
446                 argv_index_ += ParseNextParam(parsed_arg, argv_vec_[argv_index_]);
447                 if (!errstr_.empty()) {
448                     return false;
449                 }
450             }
451         }
452         return true;
453     }
454 
InitDefault()455     void InitDefault()
456     {
457         equal_flag_ = false;
458         tail_parsed_flag_ = false;
459         argv_vec_.clear();
460         argv_index_ = 0;
461         errstr_ = "";
462         // reset tail
463         for (auto tail_arg : tail_args_) {
464             switch (tail_arg->GetType()) {
465                 case PandArgType::STRING:
466                     static_cast<PandArg<std::string> *>(tail_arg)->ResetDefaultValue();
467                     break;
468                 case PandArgType::INTEGER:
469                     static_cast<PandArg<int> *>(tail_arg)->ResetDefaultValue();
470                     break;
471                 case PandArgType::DOUBLE:
472                     static_cast<PandArg<double> *>(tail_arg)->ResetDefaultValue();
473                     break;
474                 case PandArgType::BOOL:
475                     static_cast<PandArg<bool> *>(tail_arg)->ResetDefaultValue();
476                     break;
477                 case PandArgType::UINT32:
478                     static_cast<PandArg<uint32_t> *>(tail_arg)->ResetDefaultValue();
479                     break;
480                 case PandArgType::UINT64:
481                     static_cast<PandArg<uint64_t> *>(tail_arg)->ResetDefaultValue();
482                     break;
483                 case PandArgType::LIST:
484                     static_cast<PandArg<arg_list_t> *>(tail_arg)->ResetDefaultValue();
485                     break;
486                 default:
487                     break;
488             }
489         }
490         // reset remainder
491         remainder_ = arg_list_t();
492     }
493 
FindArg(std::string_view arg_name)494     PandArgBase *FindArg(std::string_view arg_name)
495     {
496         auto arg_it = args_.find(arg_name);
497         if (arg_it == args_.end()) {
498             errstr_.append("pandargs: Invalid option \"");
499             errstr_.append(arg_name);
500             errstr_.append("\"\n");
501             return nullptr;
502         }
503         return *arg_it;
504     }
505 
ParseSubArgument(PandArgCompound *parent_arg, std::string_view str)506     bool ParseSubArgument(PandArgCompound *parent_arg, std::string_view str)
507     {
508         size_t assign_pos = str.find('=');
509         std::string_view arg_name = str.substr(0, assign_pos);
510         auto arg = parent_arg->FindSubArg(arg_name);
511         if (arg == nullptr) {
512             errstr_.append("pandargs: Invalid sub-argument \"");
513             errstr_.append(arg_name);
514             errstr_.append("\"\n");
515             return false;
516         }
517 
518         if (assign_pos != std::string_view::npos) {
519             std::string_view value_str = str.substr(assign_pos + 1);
520             ParseNextParam(arg, value_str);
521             if (!errstr_.empty()) {
522                 return false;
523             }
524         } else {
525             if (arg->GetType() != PandArgType::BOOL) {
526                 errstr_.append("pandargs: Only boolean arguments might have no value \"");
527                 errstr_.append(arg_name);
528                 errstr_.append("\"\n");
529                 return false;
530             }
531             static_cast<PandArg<bool> *>(arg)->SetValue(true);
532         }
533 
534         return true;
535     }
536 
ParseCompoundArg(std::string_view argstr, size_t sep_pos)537     PandArgBase *ParseCompoundArg(std::string_view argstr, size_t sep_pos)
538     {
539         auto arg_name = argstr.substr(0, sep_pos);
540 
541         auto arg = static_cast<PandArgCompound *>(FindArg(arg_name));
542         if (arg == nullptr) {
543             return nullptr;
544         }
545 
546         arg->SetValue(true);
547 
548         auto sub_args_str = argstr.substr(sep_pos + 1);
549         size_t start = 0;
550         for (size_t pos = sub_args_str.find(',', 0); pos != std::string_view::npos;
551              start = pos + 1, pos = sub_args_str.find(',', start)) {
552             auto arg_str = sub_args_str.substr(start, pos - start);
553             if (!ParseSubArgument(arg, arg_str)) {
554                 return nullptr;
555             }
556         }
557         if (start < sub_args_str.size()) {
558             if (!ParseSubArgument(arg, sub_args_str.substr(start))) {
559                 return nullptr;
560             }
561         }
562         argv_index_++;
563         return arg;
564     }
565 
ParseNextRegularArg()566     PandArgBase *ParseNextRegularArg()
567     {
568         std::string argstr = argv_vec_[argv_index_];
569 
570         const std::size_t SEP_FOUND = NextSeparator(argstr);
571 
572         const std::size_t SEP_COMPOUND = argstr.find_first_of(':', 0);
573         if (SEP_COMPOUND != std::string::npos && (SEP_FOUND == std::string::npos || SEP_COMPOUND < SEP_FOUND)) {
574             return ParseCompoundArg(std::string_view(argstr).substr(DASH_COUNT), SEP_COMPOUND - DASH_COUNT);
575         }
576 
577         std::string arg_name;
578 
579         if (SEP_FOUND != std::string::npos) {
580             equal_flag_ = true;
581             argv_vec_[argv_index_] = argstr.substr(SEP_FOUND + 1);
582             arg_name = argstr.substr(DASH_COUNT, SEP_FOUND - DASH_COUNT);
583         } else {
584             arg_name = argstr.substr(DASH_COUNT, SEP_FOUND);
585             // check if there is next argv element to iterate into
586             if (argv_index_ + 1 < argv_vec_.size()) {
587                 argv_index_++;
588             } else {
589                 argv_vec_[argv_index_] = "";
590             }
591         }
592 
593         PandArgBase *arg = FindArg(arg_name);
594         if (arg == nullptr) {
595             return nullptr;
596         }
597 
598         if (arg->GetType() == PandArgType::COMPOUND) {
599             // It is forbidden to explicitly set compound option, e.g. `--compound=true`, must be `--compound`.
600             if (SEP_FOUND != std::string::npos) {
601                 errstr_.append("pandargs: Compound option can not be explicitly set \"");
602                 errstr_.append(arg_name);
603                 errstr_.append("\"\n");
604                 return nullptr;
605             }
606             static_cast<PandArgCompound *>(arg)->SetValue(true);
607         }
608 
609         return arg;
610     }
611 
ParseNextArg()612     PandArgBase *ParseNextArg()
613     {
614         PandArgBase *arg = nullptr;
615         std::string argstr = argv_vec_[argv_index_];
616         equal_flag_ = false;
617 
618         // NOTE: currently we have only double dash argument prefix
619         std::size_t dashes_found = argstr.find(DOUBLE_DASH);
620         if (dashes_found == 0 && argstr.size() > DASH_COUNT) {
621             // regular argument
622             return ParseNextRegularArg();
623         }
624 
625         if (dashes_found == 0 && argstr.size() == DASH_COUNT) {
626             // remainder argument
627             if (!remainder_flag_) {
628                 errstr_.append("pandargs: Remainder arguments are not enabled\n");
629                 errstr_.append("pandargs: Remainder found at literal \"");
630                 errstr_.append(argstr);
631                 errstr_.append("\"\n");
632                 return nullptr;
633             }
634 
635             argv_index_++;
636             ParseRemainder();
637         } else if (dashes_found > 0) {
638             // tail argument, N.B. std::string::npos > 0
639             if (!tail_flag_) {
640                 errstr_.append("pandargs: Tail arguments are not enabled\n");
641                 errstr_.append("pandargs: Tail found at literal \"");
642                 errstr_.append(argstr);
643                 errstr_.append("\"\n");
644                 return nullptr;
645             }
646             if (tail_parsed_flag_) {
647                 errstr_.append("pandargs: Too many tail arguments\n");
648                 return nullptr;
649             }
650             ParseTail();
651 
652             if (argv_index_ < argv_vec_.size()) {
653                 if (argv_vec_[argv_index_] != DOUBLE_DASH && !remainder_flag_) {
654                     errstr_ += "pandargs: Too many tail arguments given\n";
655                 }
656             }
657         } else {
658             errstr_.append("pandargs: Invalid option \"");
659             errstr_.append(argstr);
660             errstr_.append("\"\n");
661             UNREACHABLE();
662         }
663         return arg;
664     }
665 
ParseTail()666     void ParseTail()
667     {
668         for (auto &tail_arg : tail_args_) {
669             switch (tail_arg->GetType()) {
670                 case PandArgType::STRING:
671                     argv_index_ +=
672                         ParseStringArgParam(static_cast<PandArg<std::string> *>(tail_arg), argv_vec_[argv_index_]);
673                     break;
674                 case PandArgType::INTEGER:
675                     argv_index_ += ParseIntArgParam(static_cast<PandArg<int> *>(tail_arg), argv_vec_[argv_index_]);
676                     break;
677                 case PandArgType::DOUBLE:
678                     argv_index_ +=
679                         ParseDoubleArgParam(static_cast<PandArg<double> *>(tail_arg), argv_vec_[argv_index_]);
680                     break;
681                 case PandArgType::BOOL:
682                     argv_index_ +=
683                         ParseBoolArgParam(static_cast<PandArg<bool> *>(tail_arg), argv_vec_[argv_index_], true);
684                     break;
685                 case PandArgType::UINT32:
686                     argv_index_ +=
687                         ParseUint32ArgParam(static_cast<PandArg<uint32_t> *>(tail_arg), argv_vec_[argv_index_]);
688                     break;
689                 case PandArgType::UINT64:
690                     argv_index_ +=
691                         ParseUint64ArgParam(static_cast<PandArg<uint64_t> *>(tail_arg), argv_vec_[argv_index_]);
692                     break;
693                 case PandArgType::LIST:
694                     argv_index_ +=
695                         ParseListArgParam(static_cast<PandArg<arg_list_t> *>(tail_arg), argv_vec_[argv_index_]);
696                     break;
697                 default:
698                     errstr_.append("pandargs: Invalid tail option type: \"");
699                     errstr_.append(tail_arg->GetName());
700                     errstr_.append("\"\n");
701                     UNREACHABLE();
702                     break;
703             }
704             if (argv_index_ >= argv_vec_.size() || !errstr_.empty()) {
705                 break;
706             }
707         }
708         tail_parsed_flag_ = true;
709     }
710 
ParseRemainder()711     void ParseRemainder()
712     {
713         remainder_ = arg_list_t(argv_vec_.begin() + argv_index_, argv_vec_.end());
714         argv_index_ = argv_vec_.size();
715     }
716 
ParseNextParam(PandArgBase *arg, std::string_view argstr)717     size_t ParseNextParam(PandArgBase *arg, std::string_view argstr)
718     {
719         if (argv_index_ >= argv_vec_.size() || arg == nullptr) {
720             return 0;
721         }
722         switch (arg->GetType()) {
723             case PandArgType::STRING:
724                 return ParseStringArgParam(static_cast<PandArg<std::string> *>(arg), argstr);
725             case PandArgType::INTEGER:
726                 return ParseIntArgParam(static_cast<PandArg<int> *>(arg), argstr);
727             case PandArgType::DOUBLE:
728                 return ParseDoubleArgParam(static_cast<PandArg<double> *>(arg), argstr);
729             case PandArgType::BOOL:
730                 return ParseBoolArgParam(static_cast<PandArg<bool> *>(arg), argstr);
731             case PandArgType::UINT32:
732                 return ParseUint32ArgParam(static_cast<PandArg<uint32_t> *>(arg), argstr);
733             case PandArgType::UINT64:
734                 return ParseUint64ArgParam(static_cast<PandArg<uint64_t> *>(arg), argstr);
735             case PandArgType::LIST:
736                 return ParseListArgParam(static_cast<PandArg<arg_list_t> *>(arg), argstr);
737             case PandArgType::COMPOUND:
738                 return argstr.empty() ? 1 : 0;
739             case PandArgType::NOTYPE:
740                 errstr_.append("pandargs: Invalid option type: \"");
741                 errstr_.append(arg->GetName());
742                 errstr_.append("\"\n");
743                 UNREACHABLE();
744                 break;
745             default:
746                 UNREACHABLE();
747                 break;
748         }
749         return 0;
750     }
751 
ParseStringArgParam(PandArg<std::string> *arg, std::string_view argstr)752     std::size_t ParseStringArgParam(PandArg<std::string> *arg, std::string_view argstr)
753     {
754         arg->SetValue(std::string(argstr));
755         return 1;
756     }
757 
ParseIntArgParam(PandArg<int> *arg, std::string_view argstr)758     std::size_t ParseIntArgParam(PandArg<int> *arg, std::string_view argstr)
759     {
760         std::string param_str(argstr);
761         if (IsIntegerNumber(param_str)) {
762             int num;
763             errno = 0;
764             if (StartsWith(param_str, "0x")) {
765                 const int HEX = 16;
766                 num = std::stoi(param_str, nullptr, HEX);
767             } else {
768                 num = std::stoi(param_str);
769             }
770 
771             if (errno == ERANGE) {
772                 errstr_ +=
773                     "pandargs: \"" + arg->GetName() + "\" argument has invalid parameter value \"" + param_str + "\"\n";
774             }
775 
776             if (IsIntegerArgInRange(arg, num)) {
777                 arg->SetValue(num);
778             } else {
779                 errstr_ += "pandargs: \"" + arg->GetName() + "\" argument has out of range parameter value \"" +
780                            param_str + "\"\n";
781             }
782         } else {
783             errstr_ += "pandargs: \"" + arg->GetName() + "\" argument has out of range parameter value \"" + param_str +
784                        "\"\n";
785         }
786         return 1;
787     }
788 
ParseDoubleArgParam(PandArg<double> *arg, std::string_view argstr)789     std::size_t ParseDoubleArgParam(PandArg<double> *arg, std::string_view argstr)
790     {
791         std::string param_str(argstr);
792         if (IsRationalNumber(param_str)) {
793             arg->SetValue(std::stod(param_str));
794         } else {
795             errstr_ +=
796                 "pandargs: \"" + arg->GetName() + "\" argument has invalid parameter value \"" + param_str + "\"\n";
797         }
798         return 1;
799     }
800 
ParseBoolArgParam(PandArg<bool> *arg, std::string_view argstr, bool is_tail_param = false)801     std::size_t ParseBoolArgParam(PandArg<bool> *arg, std::string_view argstr, bool is_tail_param = false)
802     {
803         std::string param_str(argstr);
804 
805         // if not a tail argument, assume two following cases
806         if (!is_tail_param) {
807             arg->SetValue(true);
808             // bool with no param, next argument comes right after
809             if (StartsWith(param_str, DOUBLE_DASH)) {
810                 // check that bool param comes without "="
811                 if (equal_flag_) {
812                     SetBoolUnexpectedValueError(arg, param_str);
813                 }
814                 return 0;
815             }
816             // OR bool arg at the end of arguments line
817             if (param_str.empty()) {
818                 // check that bool param comes without "="
819                 if (equal_flag_) {
820                     SetBoolUnexpectedValueError(arg, param_str);
821                 }
822                 return 1;
823             }
824         }
825 
826         constexpr std::array<std::string_view, 3> TRUE_VALUES = {"on", "true", "1"};
827         constexpr std::array<std::string_view, 3> FALSE_VALUES = {"off", "false", "0"};
828 
829         for (const auto &i : TRUE_VALUES) {
830             if (param_str == i) {
831                 arg->SetValue(true);
832                 return 1;
833             }
834         }
835         for (const auto &i : FALSE_VALUES) {
836             if (param_str == i) {
837                 arg->SetValue(false);
838                 return 1;
839             }
840         }
841 
842         // if it's not a part of tail argument,
843         // assume that it's bool with no param,
844         // preceiding tail argument
845         if (!is_tail_param) {
846             // check that bool param came without "="
847             if (equal_flag_) {
848                 SetBoolUnexpectedValueError(arg, param_str);
849             } else {
850                 arg->SetValue(true);
851             }
852         } else {
853             errstr_ +=
854                 "pandargs: Tail argument " + arg->GetName() + " has unexpected parameter value " + param_str + "\n";
855             arg->ResetDefaultValue();
856         }
857 
858         return 0;
859     }
860 
ParseUint64ArgParam(PandArg<uint64_t> *arg, std::string_view argstr)861     std::size_t ParseUint64ArgParam(PandArg<uint64_t> *arg, std::string_view argstr)
862     {
863         std::string param_str(argstr);
864         if (IsUintNumber(param_str)) {
865             errno = 0;
866             uint64_t num;
867             if (StartsWith(param_str, "0x")) {
868                 const int HEX = 16;
869                 num = std::strtoull(param_str.c_str(), nullptr, HEX);
870             } else {
871                 const int DEC = 10;
872                 num = std::strtoull(param_str.c_str(), nullptr, DEC);
873             }
874             if (errno == ERANGE) {
875                 errstr_ +=
876                     "pandargs: \"" + arg->GetName() + "\" argument has invalid parameter value \"" + param_str + "\"\n";
877             }
878 
879             if (IsIntegerArgInRange<uint64_t>(arg, num)) {
880                 arg->SetValue(num);
881             } else {
882                 errstr_ += "pandargs: \"" + arg->GetName() + "\" argument has out of range parameter value \"" +
883                            param_str + "\"\n";
884             }
885         } else {
886             errstr_ +=
887                 "pandargs: \"" + arg->GetName() + "\" argument has invalid parameter value \"" + param_str + "\"\n";
888         }
889         return 1;
890     }
891 
ParseUint32ArgParam(PandArg<uint32_t> *arg, std::string_view argstr)892     std::size_t ParseUint32ArgParam(PandArg<uint32_t> *arg, std::string_view argstr)
893     {
894         std::string param_str(argstr);
895         if (IsUintNumber(param_str)) {
896             errno = 0;
897             uint32_t num;
898             if (StartsWith(param_str, "0x")) {
899                 const int HEX = 16;
900                 num = std::strtoull(param_str.c_str(), nullptr, HEX);
901             } else {
902                 const int DEC = 10;
903                 num = std::strtoull(param_str.c_str(), nullptr, DEC);
904             }
905             if (errno == ERANGE) {
906                 errstr_ +=
907                     "pandargs: \"" + arg->GetName() + "\" argument has invalid parameter value \"" + param_str + "\"\n";
908             }
909 
910             if (IsIntegerArgInRange<uint32_t>(arg, num)) {
911                 arg->SetValue(num);
912             } else {
913                 errstr_ += "pandargs: \"" + arg->GetName() + "\" argument has out of range parameter value \"" +
914                            param_str + "\"\n";
915             }
916         } else {
917             errstr_ +=
918                 "pandargs: \"" + arg->GetName() + "\" argument has invalid parameter value \"" + param_str + "\"\n";
919         }
920         return 1;
921     }
922 
ParseListArgParam(PandArg<arg_list_t> *arg, std::string_view argstr)923     std::size_t ParseListArgParam(PandArg<arg_list_t> *arg, std::string_view argstr)
924     {
925         std::string param_str(argstr);
926         arg_list_t value;
927         if (arg->WasSet()) {
928             value = arg->GetValue();
929         } else {
930             value = arg_list_t();
931         }
932         if (!arg->GetDelimiter().has_value()) {
933             value.push_back(param_str);
934             arg->SetValue(value);
935             return 1;
936         }
937         std::string delimiter = arg->GetDelimiter().value();
938         std::size_t param_str_index = 0;
939         std::size_t pos = param_str.find_first_of(delimiter, param_str_index);
940         while (pos < param_str.size()) {
941             value.push_back(param_str.substr(param_str_index, pos - param_str_index));
942             param_str_index = pos;
943             param_str_index = param_str.find_first_not_of(delimiter, param_str_index);
944             pos = param_str.find_first_of(delimiter, param_str_index);
945         }
946 
947         value.push_back(param_str.substr(param_str_index, pos - param_str_index));
948         arg->SetValue(value);
949         return 1;
950     }
951 
NextSeparator(std::string_view argstr, std::size_t pos = 0, const std::string &separarors = EQ_SEPARATOR)952     static std::size_t NextSeparator(std::string_view argstr, std::size_t pos = 0,
953                                      const std::string &separarors = EQ_SEPARATOR)
954     {
955         return argstr.find_first_of(separarors, pos);
956     }
957 
IsIntegerNumber(const std::string_view &str)958     static bool IsIntegerNumber(const std::string_view &str)
959     {
960         if (str.empty()) {
961             return false;
962         }
963         std::size_t pos = 0;
964         // look for dash if it's negative one
965         if (str[0] == '-') {
966             pos++;
967         }
968         // look for hex-style integer
969         if ((str.size() > pos + 2U) && (str.compare(pos, 2U, "0x") == 0)) {
970             pos += HEX_PREFIX_WIDTH;
971             return str.find_first_not_of("0123456789abcdefABCDEF", pos) == std::string::npos;
972         }
973         return str.find_first_not_of("0123456789", pos) == std::string::npos;
974     }
975 
IsRationalNumber(const std::string_view &str)976     static bool IsRationalNumber(const std::string_view &str)
977     {
978         if (str.empty()) {
979             return false;
980         }
981         std::size_t pos = 0;
982         // look for dash if it's negative one
983         if (str[0] == '-') {
984             pos++;
985         }
986         return str.find_first_not_of(".0123456789", pos) == std::string::npos;
987     }
988 
IsUintNumber(const std::string_view &str)989     static bool IsUintNumber(const std::string_view &str)
990     {
991         if (str.empty()) {
992             return false;
993         }
994 
995         constexpr std::string_view char_search = "0123456789";
996         constexpr std::string_view char_search_hex = "0123456789abcdef";
997         std::size_t pos = 0;
998         // look for hex-style uint_t integer
999         if (str[0] == '0' && str[1] == 'x') {
1000             pos += HEX_PREFIX_WIDTH;
1001         }
1002         return str.find_first_not_of((pos != 0) ? char_search_hex.data() : char_search.data(), pos) ==
1003                std::string::npos;
1004     }
1005 
1006     template <typename T,
1007               enable_if_t<is_same_v<T, int> || is_same_v<T, uint32_t> || is_same_v<T, uint64_t>> * = nullptr>
IsIntegerArgInRange(PandArg<T> *arg, T num)1008     bool IsIntegerArgInRange(PandArg<T> *arg, T num)
1009     {
1010         if (!(arg->GetMinMaxVal().has_value())) {
1011             return true;
1012         }
1013         std::pair<T, T> min_max = arg->GetMinMaxVal().value();
1014         return ((num >= std::get<0>(min_max)) && (num <= std::get<1>(min_max)));
1015     }
1016 
StartsWith(const std::string &haystack, const std::string &needle)1017     static bool StartsWith(const std::string &haystack, const std::string &needle)
1018     {
1019         return std::equal(needle.begin(), needle.end(), haystack.begin());
1020     }
1021 
SetBoolUnexpectedValueError(PandArg<bool> *arg, const std::string &wrongvalue)1022     void SetBoolUnexpectedValueError(PandArg<bool> *arg, const std::string &wrongvalue)
1023     {
1024         errstr_ += "pandargs: Bool argument " + arg->GetName() + " has unexpected parameter value " + wrongvalue + "\n";
1025         arg->ResetDefaultValue();
1026     }
1027 
1028     constexpr static size_t HEX_PREFIX_WIDTH = 2;
1029     constexpr static unsigned int DASH_COUNT = 2;
1030 
1031     std::vector<std::string> argv_vec_;
1032     std::size_t argv_index_ = 0;
1033     std::string errstr_ = "";
1034     bool tail_flag_ = false;
1035     bool remainder_flag_ = false;
1036     bool equal_flag_ = false;
1037     bool tail_parsed_flag_ = false;
1038     static constexpr const char *DOUBLE_DASH = "--";
1039     static constexpr const char *EQ_SEPARATOR = "=";
1040     std::set<PandArgBase *, PandArgPtrComparator> args_;
1041     std::vector<PandArgBase *> tail_args_;
1042     arg_list_t remainder_ = arg_list_t();
1043 };
1044 
1045 }  // namespace panda
1046 
1047 #endif  // LIBPANDABASE_UTILS_PANDARGS_H
1048