1 /*
2 * Copyright (C) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "argument_parser.h"
17 #include <iomanip>
18 #include <cstring>
19 #include <sstream>
20 #include <vector>
21 #include <memory>
22 #include <cstring>
23 #include "securec.h"
24 #include "sp_utils.h"
25
26 namespace OHOS::SmartPerf {
27 namespace {
28 constexpr int LINE_W = 20;
29 }
30
AddArgument(const std::string & name,ArgumentSpec info)31 void ArgumentParser::AddArgument(const std::string& name, ArgumentSpec info)
32 {
33 if (specs_.count(name)) {
34 errors_.push_back("Duplicate argument registered: " + name);
35 return;
36 }
37
38 if ((info.type == ArgType::STRING || info.type == ArgType::BOOL) && (info.min || info.max)) {
39 errors_.push_back("Only INT type supports min/max: " + name);
40 return;
41 }
42
43 specs_[name] = info;
44 }
45
Parse(const std::string & input)46 void ArgumentParser::Parse(const std::string& input)
47 {
48 std::istringstream iss(input);
49 std::vector<std::string> tokens;
50 std::string token;
51
52 while (iss >> std::quoted(token)) {
53 tokens.push_back(token);
54 }
55
56 std::vector<std::unique_ptr<char[]>> storage;
57 std::vector<char*> argv;
58 for (auto& tok : tokens) {
59 size_t tokLength = tok.size() + 1;
60 storage.emplace_back(std::make_unique<char[]>(tokLength));
61 if (strcpy_s(storage.back().get(), tokLength, tok.c_str()) != EOK) {
62 return;
63 }
64 argv.push_back(storage.back().get());
65 }
66
67 int argc = static_cast<int>(argv.size());
68 Parse(argc, argv.data());
69 }
70
HandleIntParameter(const std::string & key,std::string value,const ArgumentSpec & spec)71 void ArgumentParser::HandleIntParameter(const std::string& key, std::string value, const ArgumentSpec& spec)
72 {
73 if (spec.type == ArgType::INT) {
74 int val = SPUtilesTye::StringToSometype<int>(value);
75 if (spec.min && val < *spec.min) {
76 errors_.push_back("Value for " + key + " below min: " + std::to_string(*spec.min));
77 return;
78 }
79 if (spec.max && val > *spec.max) {
80 errors_.push_back("Value for " + key + " above max: " + std::to_string(*spec.max));
81 return;
82 }
83 values_[key] = val;
84 } else {
85 values_[key] = value;
86 }
87 }
88
Parse(int argc,char * argv[])89 void ArgumentParser::Parse(int argc, char* argv[])
90 {
91 std::set<std::string> seen_args;
92 for (int i = 1; i < argc; ++i) {
93 std::string key = argv[i];
94 if (key == "--help") {
95 helpMode_ = true;
96 return;
97 }
98 if (!specs_.count(key)) {
99 errors_.push_back("Unknown argument: " + key);
100 continue;
101 }
102 if (seen_args.count(key)) {
103 errors_.push_back("Duplicate argument: " + key);
104 const auto& spec = specs_[key];
105 if (spec.type != ArgType::BOOL && i + 1 < argc && specs_.count(argv[i + 1]) == 0) {
106 ++i;
107 }
108 continue;
109 }
110 seen_args.insert(key);
111 const auto& spec = specs_[key];
112 if (spec.type == ArgType::BOOL) {
113 values_[key] = true;
114 continue;
115 }
116 if (i + 1 >= argc || specs_.count(argv[i + 1])) {
117 errors_.push_back("Missing value for argument: " + key);
118 continue;
119 }
120 HandleIntParameter(key, std::string(argv[++i]), spec);
121 }
122 }
123
Get(const std::string & name) const124 std::optional<ArgumentParser::ArgValue> ArgumentParser::Get(const std::string& name) const
125 {
126 auto it = values_.find(name);
127 return it != values_.end() ? std::optional<ArgValue>(it->second) : std::nullopt;
128 }
129
PrintHelp() const130 void ArgumentParser::PrintHelp() const
131 {
132 std::cout << "Available arguments:\n";
133 for (const auto& [k, v] : specs_) {
134 std::cout << " " << std::left << std::setw(LINE_W) << k;
135
136 switch (v.type) {
137 case ArgType::STRING: std::cout << std::right << "(string) - "; break;
138 case ArgType::INT:
139 std::cout << "(int";
140 if (v.min) std::cout << ", min=" << *v.min;
141 if (v.max) std::cout << ", max=" << *v.max;
142 std::cout << ") - ";
143 break;
144 case ArgType::BOOL: break;
145 }
146
147 if (!v.description.empty())
148 std::cout << v.description;
149
150 std::cout << "\n";
151 }
152 }
153
Values() const154 const std::unordered_map<std::string, ArgumentParser::ArgValue>& ArgumentParser::Values() const
155 {
156 return values_;
157 }
158
Errors() const159 const std::vector<std::string>& ArgumentParser::Errors() const
160 {
161 return errors_;
162 }
163
Ok() const164 bool ArgumentParser::Ok() const
165 {
166 return errors_.empty();
167 }
168
IsHelpMode() const169 bool ArgumentParser::IsHelpMode() const
170 {
171 return helpMode_;
172 }
173
SetValue(const std::string & key,const ArgValue & value)174 void ArgumentParser::SetValue(const std::string& key, const ArgValue& value)
175 {
176 values_[key] = value;
177 }
178 }