1 #include <sdbusplus/message/native_types.hpp> 2 3 #include <algorithm> 4 #include <array> 5 #include <cctype> 6 #include <cstdint> 7 8 namespace sdbusplus 9 { 10 namespace message 11 { 12 namespace details 13 { 14 15 constexpr std::array<char, 16> hex{'0', '1', '2', '3', '4', '5', '6', '7', 16 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; 17 18 constexpr std::array<int8_t, 256> unhex = [] { 19 std::array<int8_t, 256> ret{}; 20 for (size_t i = 0; i < ret.size(); ++i) 21 { 22 ret[i] = -1; 23 } 24 for (char i = 0; i < 10; ++i) 25 { 26 ret['0' + i] = i; 27 } 28 for (char i = 0; i < 6; ++i) 29 { 30 ret['A' + i] = i + 10; 31 ret['a' + i] = i + 10; 32 } 33 return ret; 34 }(); 35 36 inline bool pathShouldEscape(char c) 37 { 38 return !std::isalnum(c); 39 } 40 41 inline bool pathRequiresEscape(char c) 42 { 43 return pathShouldEscape(c) && c != '_'; 44 } 45 46 inline void pathAppendEscape(std::string& s, char c) 47 { 48 s.append(1, '_'); 49 s.append(1, hex[(c >> 4) & 0xf]); 50 s.append(1, hex[c & 0xf]); 51 } 52 53 std::string string_path_wrapper::filename() const 54 { 55 std::string_view strv(str); 56 size_t firstIndex = strv.rfind('/'); 57 58 // Dbus paths must start with /, if we don't find one, it's an error 59 if (firstIndex == std::string::npos) 60 { 61 return ""; 62 } 63 auto filename = strv.substr(firstIndex + 1); 64 65 // If we don't see that this was encoded by sdbusplus, return the naive 66 // version of the filename path. 67 if (filename[0] != '_') 68 { 69 return std::string(filename); 70 } 71 72 std::string out; 73 out.reserve(filename.size()); 74 for (size_t i = 0; i < filename.size(); ++i) 75 { 76 if (filename[i] != '_') 77 { 78 out.append(1, filename[i]); 79 continue; 80 } 81 if (i + 2 >= filename.size()) 82 { 83 return ""; 84 } 85 auto ch = unhex[filename[i + 1]]; 86 auto cl = unhex[filename[i + 2]]; 87 if (ch == -1 || cl == -1) 88 { 89 return ""; 90 } 91 out.append(1, static_cast<char>((ch << 4) | cl)); 92 i += 2; 93 } 94 return out; 95 } 96 97 string_path_wrapper string_path_wrapper::parent_path() const 98 { 99 auto index = str.rfind('/'); 100 if (index == std::string::npos) 101 { 102 return string_path_wrapper("/"); 103 } 104 if (index <= 1) 105 { 106 return string_path_wrapper("/"); 107 } 108 109 return str.substr(0, index); 110 } 111 112 string_path_wrapper string_path_wrapper::operator/(std::string_view extId) const 113 { 114 string_path_wrapper out; 115 out.str.reserve(str.size() + 1 + extId.size() * 3); 116 out.str.append(str); 117 return out /= extId; 118 } 119 120 string_path_wrapper& string_path_wrapper::operator/=(std::string_view extId) 121 { 122 str.reserve(str.size() + 1 + extId.size() * 3); 123 if (!str.empty() && str[str.size() - 1] != '/') 124 { 125 str.append(1, '/'); 126 } 127 if (extId.empty() || 128 (!pathShouldEscape(extId[0]) && 129 std::none_of(extId.begin() + 1, extId.end(), pathRequiresEscape))) 130 { 131 str.append(extId); 132 return *this; 133 } 134 pathAppendEscape(str, extId[0]); 135 for (auto c : extId.substr(1)) 136 { 137 if (pathShouldEscape(c)) 138 { 139 pathAppendEscape(str, c); 140 } 141 else 142 { 143 str.append(1, c); 144 } 145 } 146 return *this; 147 } 148 149 } // namespace details 150 } // namespace message 151 } // namespace sdbusplus 152