• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 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:
47     explicit PandArgBase(std::string name, std::string desc, PandArgType type = PandArgType::NOTYPE)
name_(std::move (name))48         : name_(std::move(name)), desc_(std::move(desc)), type_(type)
49     {
50     }
51 
GetType()52     PandArgType GetType() const
53     {
54         return type_;
55     }
56 
GetName()57     std::string GetName() const
58     {
59         return name_;
60     }
61 
GetDesc()62     std::string GetDesc() const
63     {
64         return desc_;
65     }
66 
WasSet()67     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()118     T GetValue() const
119     {
120         return real_val_;
121     }
122 
GetDefaultValue()123     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 
ResetDefaultValue()138     void ResetDefaultValue() override
139     {
140         real_val_ = default_val_;
141     }
142 
GetDelimiter()143     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 
ResetDefaultValue()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()210     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()300     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()315     bool IsTailEnabled() const
316     {
317         return tail_flag_;
318     }
319 
GetTailSize()320     std::size_t GetTailSize() const
321     {
322         return tail_args_.size();
323     }
324 
EnableRemainder()325     void EnableRemainder() noexcept
326     {
327         remainder_flag_ = true;
328     }
329 
DisableRemainder()330     void DisableRemainder() noexcept
331     {
332         remainder_flag_ = false;
333     }
334 
IsRemainderEnabled()335     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)345     bool IsArgSet(PandArgBase *arg) const
346     {
347         return args_.find(arg) != args_.end();
348     }
349 
IsArgSet(const std::string & arg_name)350     bool IsArgSet(const std::string &arg_name) const
351     {
352         return args_.find(arg_name) != args_.end();
353     }
354 
GetHelpString()355     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;
operatorPandArgPtrComparator424         bool operator()(const PandArgBase *lhs, const PandArgBase *rhs) const
425         {
426             return lhs->GetName() < rhs->GetName();
427         }
operatorPandArgPtrComparator428         bool operator()(std::string_view lhs, const PandArgBase *rhs) const
429         {
430             return lhs < rhs->GetName();
431         }
operatorPandArgPtrComparator432         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 
658         } else {
659             errstr_.append("pandargs: Invalid option \"");
660             errstr_.append(argstr);
661             errstr_.append("\"\n");
662             UNREACHABLE();
663         }
664         return arg;
665     }
666 
ParseTail()667     void ParseTail()
668     {
669         for (auto &tail_arg : tail_args_) {
670             switch (tail_arg->GetType()) {
671                 case PandArgType::STRING:
672                     argv_index_ +=
673                         ParseStringArgParam(static_cast<PandArg<std::string> *>(tail_arg), argv_vec_[argv_index_]);
674                     break;
675                 case PandArgType::INTEGER:
676                     argv_index_ += ParseIntArgParam(static_cast<PandArg<int> *>(tail_arg), argv_vec_[argv_index_]);
677                     break;
678                 case PandArgType::DOUBLE:
679                     argv_index_ +=
680                         ParseDoubleArgParam(static_cast<PandArg<double> *>(tail_arg), argv_vec_[argv_index_]);
681                     break;
682                 case PandArgType::BOOL:
683                     argv_index_ +=
684                         ParseBoolArgParam(static_cast<PandArg<bool> *>(tail_arg), argv_vec_[argv_index_], true);
685                     break;
686                 case PandArgType::UINT32:
687                     argv_index_ +=
688                         ParseUint32ArgParam(static_cast<PandArg<uint32_t> *>(tail_arg), argv_vec_[argv_index_]);
689                     break;
690                 case PandArgType::UINT64:
691                     argv_index_ +=
692                         ParseUint64ArgParam(static_cast<PandArg<uint64_t> *>(tail_arg), argv_vec_[argv_index_]);
693                     break;
694                 case PandArgType::LIST:
695                     argv_index_ +=
696                         ParseListArgParam(static_cast<PandArg<arg_list_t> *>(tail_arg), argv_vec_[argv_index_]);
697                     break;
698                 default:
699                     errstr_.append("pandargs: Invalid tail option type: \"");
700                     errstr_.append(tail_arg->GetName());
701                     errstr_.append("\"\n");
702                     UNREACHABLE();
703                     break;
704             }
705             if (argv_index_ >= argv_vec_.size() || !errstr_.empty()) {
706                 break;
707             }
708         }
709         tail_parsed_flag_ = true;
710     }
711 
ParseRemainder()712     void ParseRemainder()
713     {
714         remainder_ = arg_list_t(argv_vec_.begin() + argv_index_, argv_vec_.end());
715         argv_index_ = argv_vec_.size();
716     }
717 
ParseNextParam(PandArgBase * arg,std::string_view argstr)718     size_t ParseNextParam(PandArgBase *arg, std::string_view argstr)
719     {
720         if (argv_index_ >= argv_vec_.size() || arg == nullptr) {
721             return 0;
722         }
723         switch (arg->GetType()) {
724             case PandArgType::STRING:
725                 return ParseStringArgParam(static_cast<PandArg<std::string> *>(arg), argstr);
726             case PandArgType::INTEGER:
727                 return ParseIntArgParam(static_cast<PandArg<int> *>(arg), argstr);
728             case PandArgType::DOUBLE:
729                 return ParseDoubleArgParam(static_cast<PandArg<double> *>(arg), argstr);
730             case PandArgType::BOOL:
731                 return ParseBoolArgParam(static_cast<PandArg<bool> *>(arg), argstr);
732             case PandArgType::UINT32:
733                 return ParseUint32ArgParam(static_cast<PandArg<uint32_t> *>(arg), argstr);
734             case PandArgType::UINT64:
735                 return ParseUint64ArgParam(static_cast<PandArg<uint64_t> *>(arg), argstr);
736             case PandArgType::LIST:
737                 return ParseListArgParam(static_cast<PandArg<arg_list_t> *>(arg), argstr);
738             case PandArgType::COMPOUND:
739                 return argstr.empty() ? 1 : 0;
740             case PandArgType::NOTYPE:
741                 errstr_.append("pandargs: Invalid option type: \"");
742                 errstr_.append(arg->GetName());
743                 errstr_.append("\"\n");
744                 UNREACHABLE();
745                 break;
746             default:
747                 UNREACHABLE();
748                 break;
749         }
750         return 0;
751     }
752 
ParseStringArgParam(PandArg<std::string> * arg,std::string_view argstr)753     std::size_t ParseStringArgParam(PandArg<std::string> *arg, std::string_view argstr)
754     {
755         arg->SetValue(std::string(argstr));
756         return 1;
757     }
758 
ParseIntArgParam(PandArg<int> * arg,std::string_view argstr)759     std::size_t ParseIntArgParam(PandArg<int> *arg, std::string_view argstr)
760     {
761         std::string param_str(argstr);
762         if (IsIntegerNumber(param_str)) {
763             int num;
764             errno = 0;
765             if (StartsWith(param_str, "0x")) {
766                 const int HEX = 16;
767                 num = std::stoi(param_str, nullptr, HEX);
768             } else {
769                 num = std::stoi(param_str);
770             }
771 
772             if (errno == ERANGE) {
773                 errstr_ +=
774                     "pandargs: \"" + arg->GetName() + "\" argument has invalid parameter value \"" + param_str + "\"\n";
775             }
776 
777             if (IsIntegerArgInRange(arg, num)) {
778                 arg->SetValue(num);
779             } else {
780                 errstr_ += "pandargs: \"" + arg->GetName() + "\" argument has out of range parameter value \"" +
781                            param_str + "\"\n";
782             }
783         } else {
784             errstr_ += "pandargs: \"" + arg->GetName() + "\" argument has out of range parameter value \"" + param_str +
785                        "\"\n";
786         }
787         return 1;
788     }
789 
ParseDoubleArgParam(PandArg<double> * arg,std::string_view argstr)790     std::size_t ParseDoubleArgParam(PandArg<double> *arg, std::string_view argstr)
791     {
792         std::string param_str(argstr);
793         if (IsRationalNumber(param_str)) {
794             arg->SetValue(std::stod(param_str));
795         } else {
796             errstr_ +=
797                 "pandargs: \"" + arg->GetName() + "\" argument has invalid parameter value \"" + param_str + "\"\n";
798         }
799         return 1;
800     }
801 
802     std::size_t ParseBoolArgParam(PandArg<bool> *arg, std::string_view argstr, bool is_tail_param = false)
803     {
804         std::string param_str(argstr);
805 
806         // if not a tail argument, assume two following cases
807         if (!is_tail_param) {
808             arg->SetValue(true);
809             // bool with no param, next argument comes right after
810             if (StartsWith(param_str, DOUBLE_DASH)) {
811                 // check that bool param comes without "="
812                 if (equal_flag_) {
813                     SetBoolUnexpectedValueError(arg, param_str);
814                 }
815                 return 0;
816             }
817             // OR bool arg at the end of arguments line
818             if (param_str.empty()) {
819                 // check that bool param comes without "="
820                 if (equal_flag_) {
821                     SetBoolUnexpectedValueError(arg, param_str);
822                 }
823                 return 1;
824             }
825         }
826 
827         constexpr std::array<std::string_view, 3> TRUE_VALUES = {"on", "true", "1"};
828         constexpr std::array<std::string_view, 3> FALSE_VALUES = {"off", "false", "0"};
829 
830         for (const auto &i : TRUE_VALUES) {
831             if (param_str == i) {
832                 arg->SetValue(true);
833                 return 1;
834             }
835         }
836         for (const auto &i : FALSE_VALUES) {
837             if (param_str == i) {
838                 arg->SetValue(false);
839                 return 1;
840             }
841         }
842 
843         // if it's not a part of tail argument,
844         // assume that it's bool with no param,
845         // preceiding tail argument
846         if (!is_tail_param) {
847             // check that bool param came without "="
848             if (equal_flag_) {
849                 SetBoolUnexpectedValueError(arg, param_str);
850             } else {
851                 arg->SetValue(true);
852             }
853         } else {
854             errstr_ +=
855                 "pandargs: Tail argument " + arg->GetName() + " has unexpected parameter value " + param_str + "\n";
856             arg->ResetDefaultValue();
857         }
858 
859         return 0;
860     }
861 
ParseUint64ArgParam(PandArg<uint64_t> * arg,std::string_view argstr)862     std::size_t ParseUint64ArgParam(PandArg<uint64_t> *arg, std::string_view argstr)
863     {
864         std::string param_str(argstr);
865         if (IsUintNumber(param_str)) {
866             errno = 0;
867             uint64_t num;
868             if (StartsWith(param_str, "0x")) {
869                 const int HEX = 16;
870                 num = std::strtoull(param_str.c_str(), nullptr, HEX);
871             } else {
872                 const int DEC = 10;
873                 num = std::strtoull(param_str.c_str(), nullptr, DEC);
874             }
875             if (errno == ERANGE) {
876                 errstr_ +=
877                     "pandargs: \"" + arg->GetName() + "\" argument has invalid parameter value \"" + param_str + "\"\n";
878             }
879 
880             if (IsIntegerArgInRange<uint64_t>(arg, num)) {
881                 arg->SetValue(num);
882             } else {
883                 errstr_ += "pandargs: \"" + arg->GetName() + "\" argument has out of range parameter value \"" +
884                            param_str + "\"\n";
885             }
886         } else {
887             errstr_ +=
888                 "pandargs: \"" + arg->GetName() + "\" argument has invalid parameter value \"" + param_str + "\"\n";
889         }
890         return 1;
891     }
892 
ParseUint32ArgParam(PandArg<uint32_t> * arg,std::string_view argstr)893     std::size_t ParseUint32ArgParam(PandArg<uint32_t> *arg, std::string_view argstr)
894     {
895         std::string param_str(argstr);
896         if (IsUintNumber(param_str)) {
897             errno = 0;
898             uint32_t num;
899             if (StartsWith(param_str, "0x")) {
900                 const int HEX = 16;
901                 num = std::strtoull(param_str.c_str(), nullptr, HEX);
902             } else {
903                 const int DEC = 10;
904                 num = std::strtoull(param_str.c_str(), nullptr, DEC);
905             }
906             if (errno == ERANGE) {
907                 errstr_ +=
908                     "pandargs: \"" + arg->GetName() + "\" argument has invalid parameter value \"" + param_str + "\"\n";
909             }
910 
911             if (IsIntegerArgInRange<uint32_t>(arg, num)) {
912                 arg->SetValue(num);
913             } else {
914                 errstr_ += "pandargs: \"" + arg->GetName() + "\" argument has out of range parameter value \"" +
915                            param_str + "\"\n";
916             }
917         } else {
918             errstr_ +=
919                 "pandargs: \"" + arg->GetName() + "\" argument has invalid parameter value \"" + param_str + "\"\n";
920         }
921         return 1;
922     }
923 
ParseListArgParam(PandArg<arg_list_t> * arg,std::string_view argstr)924     std::size_t ParseListArgParam(PandArg<arg_list_t> *arg, std::string_view argstr)
925     {
926         std::string param_str(argstr);
927         arg_list_t value;
928         if (arg->WasSet()) {
929             value = arg->GetValue();
930         } else {
931             value = arg_list_t();
932         }
933         if (!arg->GetDelimiter().has_value()) {
934             value.push_back(param_str);
935             arg->SetValue(value);
936             return 1;
937         }
938         std::string delimiter = arg->GetDelimiter().value();
939         std::size_t param_str_index = 0;
940         std::size_t pos = param_str.find_first_of(delimiter, param_str_index);
941         while (pos < param_str.size()) {
942             value.push_back(param_str.substr(param_str_index, pos - param_str_index));
943             param_str_index = pos;
944             param_str_index = param_str.find_first_not_of(delimiter, param_str_index);
945             pos = param_str.find_first_of(delimiter, param_str_index);
946         }
947 
948         value.push_back(param_str.substr(param_str_index, pos - param_str_index));
949         arg->SetValue(value);
950         return 1;
951     }
952 
953     static std::size_t NextSeparator(std::string_view argstr, std::size_t pos = 0,
954                                      const std::string &separarors = EQ_SEPARATOR)
955     {
956         return argstr.find_first_of(separarors, pos);
957     }
958 
IsIntegerNumber(const std::string_view & str)959     static bool IsIntegerNumber(const std::string_view &str)
960     {
961         if (str.empty()) {
962             return false;
963         }
964         std::size_t pos = 0;
965         // look for dash if it's negative one
966         if (str[0] == '-') {
967             pos++;
968         }
969         // look for hex-style integer
970         if (str[0] == '0' && str[1] == 'x') {
971             pos += HEX_PREFIX_WIDTH;
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