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
__anon685ec59d0102null18 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
pathShouldEscape(char c)36 inline bool pathShouldEscape(char c)
37 {
38 return !std::isalnum(c);
39 }
40
pathRequiresEscape(char c)41 inline bool pathRequiresEscape(char c)
42 {
43 return pathShouldEscape(c) && c != '_';
44 }
45
pathAppendEscape(std::string & s,char c)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
filename() const53 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.empty() || 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
parent_path() const97 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
operator /(std::string_view extId) const112 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
operator /=(std::string_view extId)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