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