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 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 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 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 121 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 136 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 { 165 explicit MatchProbeForwarder(const nlohmann::json& probe) : probeRef(probe) 166 {} 167 const nlohmann::json& probeRef; 168 169 template <typename T> 170 bool operator()(const T& dbusValue) const 171 { 172 return MatchProbe<T>::match(probeRef, dbusValue); 173 } 174 }; 175 176 bool matchProbe(const nlohmann::json& probe, const DBusValueVariant& dbusValue) 177 { 178 return std::visit(MatchProbeForwarder(probe), dbusValue); 179 } 180 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 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 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 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