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