1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright 2017 Intel Corporation
3
4 #include "utils.hpp"
5
6 #include <boost/container/flat_map.hpp>
7 #include <boost/lexical_cast.hpp>
8 #include <phosphor-logging/lg2.hpp>
9 #include <sdbusplus/bus/match.hpp>
10
11 #include <algorithm>
12 #include <cctype>
13 #include <filesystem>
14 #include <map>
15 #include <ranges>
16 #include <regex>
17 #include <string_view>
18 #include <utility>
19
20 namespace fs = std::filesystem;
21
findFiles(const fs::path & dirPath,const std::string & matchString,std::vector<fs::path> & foundPaths)22 bool findFiles(const fs::path& dirPath, const std::string& matchString,
23 std::vector<fs::path>& foundPaths)
24 {
25 if (!fs::exists(dirPath))
26 {
27 return false;
28 }
29
30 std::regex search(matchString);
31 std::smatch match;
32 for (const auto& p : fs::directory_iterator(dirPath))
33 {
34 std::string path = p.path().string();
35 if (std::regex_search(path, match, search))
36 {
37 foundPaths.emplace_back(p.path());
38 }
39 }
40 return true;
41 }
42
findFiles(const std::vector<fs::path> && dirPaths,const std::string & matchString,std::vector<fs::path> & foundPaths)43 bool findFiles(const std::vector<fs::path>&& dirPaths,
44 const std::string& matchString,
45 std::vector<fs::path>& foundPaths)
46 {
47 std::map<fs::path, fs::path> paths;
48 std::regex search(matchString);
49 std::smatch match;
50 for (const auto& dirPath : dirPaths)
51 {
52 if (!fs::exists(dirPath))
53 {
54 continue;
55 }
56
57 for (const auto& p : fs::recursive_directory_iterator(dirPath))
58 {
59 std::error_code ec;
60 if (p.is_directory(ec))
61 {
62 continue;
63 }
64
65 std::string path = p.path().string();
66 if (std::regex_search(path, match, search))
67 {
68 paths[p.path().filename()] = p.path();
69 }
70 }
71 }
72
73 for (const auto& [key, value] : paths)
74 {
75 foundPaths.emplace_back(value);
76 }
77
78 return !foundPaths.empty();
79 }
80
getI2cDevicePaths(const fs::path & dirPath,boost::container::flat_map<size_t,fs::path> & busPaths)81 bool getI2cDevicePaths(const fs::path& dirPath,
82 boost::container::flat_map<size_t, fs::path>& busPaths)
83 {
84 if (!fs::exists(dirPath))
85 {
86 return false;
87 }
88
89 // Regex for matching the path
90 std::regex searchPath(std::string(R"(i2c-\d+$)"));
91 // Regex for matching the bus numbers
92 std::regex searchBus(std::string(R"(\w[^-]*$)"));
93 std::smatch matchPath;
94 std::smatch matchBus;
95 for (const auto& p : fs::directory_iterator(dirPath))
96 {
97 std::string path = p.path().string();
98 if (std::regex_search(path, matchPath, searchPath))
99 {
100 if (std::regex_search(path, matchBus, searchBus))
101 {
102 size_t bus = stoul(*matchBus.begin());
103 busPaths.insert(std::pair<size_t, fs::path>(bus, p.path()));
104 }
105 }
106 }
107
108 return true;
109 }
110
111 /// \brief JSON/DBus matching Callable for std::variant (visitor)
112 ///
113 /// Default match JSON/DBus match implementation
114 /// \tparam T The concrete DBus value type from DBusValueVariant
115 template <typename T>
116 struct MatchProbe
117 {
118 /// \param probe the probe statement to match against
119 /// \param value the property value being matched to a probe
120 /// \return true if the dbusValue matched the probe otherwise false
matchMatchProbe121 static bool match(const nlohmann::json& probe, const T& value)
122 {
123 return probe == value;
124 }
125 };
126
127 /// \brief JSON/DBus matching Callable for std::variant (visitor)
128 ///
129 /// std::string specialization of MatchProbe enabling regex matching
130 template <>
131 struct MatchProbe<std::string>
132 {
133 /// \param probe the probe statement to match against
134 /// \param value the string value being matched to a probe
135 /// \return true if the dbusValue matched the probe otherwise false
matchMatchProbe136 static bool match(const nlohmann::json& probe, const std::string& value)
137 {
138 if (probe.is_string())
139 {
140 try
141 {
142 std::regex search(probe);
143 std::smatch regMatch;
144 return std::regex_search(value, regMatch, search);
145 }
146 catch (const std::regex_error&)
147 {
148 lg2::error(
149 "Syntax error in regular expression: {PROBE} will never match",
150 "PROBE", probe);
151 }
152 }
153
154 // Skip calling nlohmann here, since it will never match a non-string
155 // to a std::string
156 return false;
157 }
158 };
159
160 /// \brief Forwarding JSON/DBus matching Callable for std::variant (visitor)
161 ///
162 /// Forward calls to the correct template instantiation of MatchProbe
163 struct MatchProbeForwarder
164 {
MatchProbeForwarderMatchProbeForwarder165 explicit MatchProbeForwarder(const nlohmann::json& probe) : probeRef(probe)
166 {}
167 const nlohmann::json& probeRef;
168
169 template <typename T>
operator ()MatchProbeForwarder170 bool operator()(const T& dbusValue) const
171 {
172 return MatchProbe<T>::match(probeRef, dbusValue);
173 }
174 };
175
matchProbe(const nlohmann::json & probe,const DBusValueVariant & dbusValue)176 bool matchProbe(const nlohmann::json& probe, const DBusValueVariant& dbusValue)
177 {
178 return std::visit(MatchProbeForwarder(probe), dbusValue);
179 }
180
split(std::string_view str,char delim)181 std::vector<std::string> split(std::string_view str, char delim)
182 {
183 std::vector<std::string> out;
184
185 size_t start = 0;
186 while (start <= str.size())
187 {
188 size_t end = str.find(delim, start);
189 if (end == std::string_view::npos)
190 {
191 out.emplace_back(str.substr(start));
192 break;
193 }
194
195 out.emplace_back(str.substr(start, end - start));
196 start = end + 1;
197 }
198
199 return out;
200 }
201
iReplaceAll(std::string & str,std::string_view search,std::string_view replace)202 void iReplaceAll(std::string& str, std::string_view search,
203 std::string_view replace)
204 {
205 if (search.empty() || search == replace)
206 {
207 return;
208 }
209
210 while (true)
211 {
212 std::ranges::subrange<std::string::iterator> match =
213 iFindFirst(str, search);
214 if (!match)
215 {
216 break;
217 }
218
219 str.replace(match.begin(), match.end(), replace.begin(), replace.end());
220 }
221 }
222
replaceAll(std::string & str,std::string_view search,std::string_view replace)223 void replaceAll(std::string& str, std::string_view search,
224 std::string_view replace)
225 {
226 if (search.empty())
227 {
228 return;
229 }
230
231 size_t pos = 0;
232 while ((pos = str.find(search, pos)) != std::string::npos)
233 {
234 str.replace(pos, search.size(), replace);
235 pos += replace.size();
236 }
237 }
238
toLowerCopy(std::string_view str)239 std::string toLowerCopy(std::string_view str)
240 {
241 std::string result(str);
242 std::transform(result.begin(), result.end(), result.begin(),
243 [](unsigned char c) { return asciiToLower(c); });
244 return result;
245 }
246