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