Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief Implementation of string encoding and decoding class
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : *
8 : */
9 :
10 : #include <vector>
11 :
12 : #include "coder.hpp"
13 :
14 : namespace
15 : {
16 : /**
17 : * @brief This function maps hex characters to integer numbers.
18 : *
19 : * @pre The specified character has to be between
20 : *
21 : * - `'0'`–`'9'`,
22 : * - `'a'`-`'f'`, or
23 : * - `'A'`-`'F'`
24 : *
25 : * .
26 : *
27 : * @param character This argument specifies the (hexadecimal) character this function converts.
28 : *
29 : * @return An integer number between `0` and `15` if the precondition is valid or `0` otherwise
30 : */
31 : int elektraHexcodeConvFromHex (char const character)
32 : {
33 3962 : if (character >= '0' && character <= '9') return character - '0';
34 1015 : if (character >= 'a' && character <= 'f') return character - 'a' + 10;
35 984 : if (character >= 'A' && character <= 'F') return character - 'A' + 10;
36 :
37 : return 0; /* Unknown escape char */
38 : }
39 : } // end namespace
40 :
41 : namespace elektra
42 : {
43 : using CppKeySet = kdb::KeySet;
44 : using CppKey = kdb::Key;
45 :
46 : /**
47 : * @brief This function sets default values for the encoding and decoding character mapping.
48 : */
49 149 : void Coder::setDefaultConfig ()
50 : {
51 : unsigned char pairs[][2] = { { '\b', 'b' }, { '\t', 't' }, { '\n', 'n' }, { '\v', 'v' }, { '\f', 'f' },
52 149 : { '\r', 'r' }, { '\\', '\\' }, { '\'', '\'' }, { '\"', '"' }, { '\0', '0' } };
53 :
54 1639 : for (size_t pair = 0; pair < sizeof (pairs) / sizeof (pairs[0]); pair++)
55 : {
56 1490 : unsigned char character = pairs[pair][0];
57 1490 : unsigned char replacement = pairs[pair][1];
58 :
59 2980 : encode[character] = replacement;
60 2980 : decode[replacement] = character;
61 : }
62 149 : }
63 :
64 : /**
65 : * @brief This function sets values for the encoding and decoding character mapping.
66 : *
67 : * @param config This key set stores configuration values for the character mapping.
68 : * @param root This key stores the root key for the character mapping stored in `config`.
69 : */
70 321 : void Coder::readConfig (CppKeySet const & config, CppKey const & root)
71 : {
72 3329 : for (auto key : config)
73 : {
74 : /* Ignore keys that are not directly below the config root key or have an incorrect size */
75 4132 : if (!key->isDirectBelow (root) || key->getBaseNameSize () != 3 || key->getBinarySize () != 3) continue;
76 :
77 4415 : int character = elektraHexcodeConvFromHex (key->getBaseName ()[1]);
78 4415 : character += elektraHexcodeConvFromHex (key->getBaseName ()[0]) * 16;
79 :
80 4415 : int replacement = elektraHexcodeConvFromHex (key->get<string> ()[1]);
81 4415 : replacement += elektraHexcodeConvFromHex (key->get<string> ()[0]) * 16;
82 :
83 : /* Hexencode this character! */
84 1766 : encode[character & 255] = replacement;
85 1766 : decode[replacement & 255] = character;
86 : }
87 321 : }
88 :
89 : /**
90 : * @brief This function replaces unescaped characters in a string with escaped characters.
91 : *
92 : * @param text This variable stores the string this function escapes.
93 : *
94 : * @return The encoded string
95 : */
96 253 : string Coder::encodeString (string const & text)
97 : {
98 506 : vector<unsigned char> encoded;
99 :
100 2401 : for (unsigned char character : text)
101 : {
102 2778 : if (encode[character])
103 : {
104 84 : encoded.push_back (escapeCharacter);
105 168 : encoded.push_back (encode[character]);
106 : }
107 : else
108 : {
109 1305 : encoded.push_back (character);
110 : }
111 : }
112 :
113 1518 : return { encoded.begin (), encoded.end () };
114 : }
115 :
116 : /**
117 : * @brief This function replaces escaped characters in a string with unescaped characters.
118 : *
119 : * @param text This string stores the string this function decodes.
120 : *
121 : * @return The decoded string
122 : */
123 626 : string Coder::decodeString (string const & text)
124 : {
125 1252 : vector<char> decoded;
126 :
127 626 : auto character = text.begin ();
128 :
129 8466 : while (character != text.end ())
130 : {
131 3607 : if (*character == escapeCharacter)
132 : {
133 131 : ++character;
134 393 : decoded.push_back (decode[*character]);
135 : }
136 : else
137 : {
138 3476 : decoded.push_back (*character);
139 : }
140 : ++character;
141 : }
142 :
143 3756 : return { decoded.begin (), decoded.end () };
144 : }
145 :
146 : /**
147 : * @brief This function replaces unescaped characters in a key value with escaped characters.
148 : *
149 : * The function only modifies the key value if it has type `string`.
150 : *
151 : * @param key This key stores the value this function encodes.
152 : */
153 59 : void Coder::encodeValue (CppKey & key)
154 : {
155 279 : if (key.isString ()) key.setString (encodeString (key.get<string> ()));
156 59 : }
157 :
158 : /**
159 : * @brief This function replaces escaped characters in a key value with unescaped characters.
160 : *
161 : * The function only modifies the key value if it has type `string`.
162 : *
163 : * @param key This key holds the value this function decodes.
164 : */
165 143 : void Coder::decodeValue (CppKey & key)
166 : {
167 667 : if (key.isString ()) key.setString (decodeString (key.get<string> ()));
168 143 : }
169 :
170 : /**
171 : * @brief This function replaces unescaped characters in a key name with escaped characters.
172 : *
173 : * @param key This `Key` stores a name possibly containing unescaped special characters.
174 : *
175 : * @return A copy of `key` containing an escaped version of the name of `key`
176 : */
177 59 : CppKey Coder::encodeName (CppKey const & key)
178 : {
179 118 : CppKey escaped{ key.dup () };
180 118 : escaped.setName (key.getNamespace ());
181 : auto keyIterator = key.begin ();
182 :
183 969 : while (++keyIterator != key.end ())
184 : {
185 594 : escaped.addBaseName (encodeString (*keyIterator));
186 : }
187 : ELEKTRA_LOG_DEBUG ("Encoded name of “%s” is “%s”", key.getName ().c_str (), escaped.getName ().c_str ());
188 59 : return escaped;
189 : }
190 :
191 : /**
192 : * @brief This function replaces escaped characters in a key name with unescaped characters.
193 : *
194 : * @param key This `Key` stores a name possibly containing escaped special characters.
195 : *
196 : * @return A copy of `key` containing an unescaped version of the name of `key`
197 : */
198 143 : CppKey Coder::decodeName (CppKey const & key)
199 : {
200 286 : CppKey unescaped{ key.dup () };
201 286 : unescaped.setName (key.getNamespace ());
202 : auto keyIterator = key.begin ();
203 :
204 2409 : while (++keyIterator != key.end ())
205 : {
206 1485 : unescaped.addBaseName (decodeString (*keyIterator));
207 : }
208 : ELEKTRA_LOG_DEBUG ("Decoded name of “%s” is “%s”", key.getName ().c_str (), unescaped.getName ().c_str ());
209 143 : return unescaped;
210 : }
211 :
212 : /**
213 : * @brief This constructor creates a new object used to decode and encode key sets.
214 : *
215 : * @param config This key set contains configuration values for decoding and encoding such as mapping data and the escape character.
216 : */
217 1410 : Coder::Coder (CppKeySet config)
218 : {
219 1880 : encode = vector<unsigned char> (256);
220 1880 : decode = vector<unsigned char> (256);
221 :
222 470 : escapeCharacter = '\\';
223 2820 : CppKey const escape = config.lookup ("/escape", 0);
224 900 : if (escape && escape.getBaseNameSize () && escape.getBinarySize () == 3)
225 : {
226 860 : int escapeChar = elektraHexcodeConvFromHex (escape.get<string> ()[1]);
227 860 : escapeChar += elektraHexcodeConvFromHex (escape.get<string> ()[0]) * 16;
228 :
229 215 : escapeCharacter = escapeChar & 255;
230 : }
231 : ELEKTRA_LOG_DEBUG ("Use “%c” as escape character", escapeCharacter);
232 :
233 940 : CppKey const root = CppKey{ "/chars", KEY_END };
234 1880 : CppKeySet mappingConfig{ config.cut (root) };
235 :
236 : #ifdef HAVE_LOGGER
237 : for (auto key : mappingConfig)
238 : {
239 : ELEKTRA_LOG_DEBUG ("Decode 0x%s as 0x%s ", key->getBaseName ().c_str (), key.getString ().c_str ());
240 : }
241 : #endif
242 :
243 470 : if (mappingConfig.size () > 0)
244 : {
245 321 : readConfig (mappingConfig, root);
246 : }
247 : else
248 : {
249 149 : setDefaultConfig ();
250 : }
251 470 : }
252 :
253 : /**
254 : * @brief This function escapes special characters in key names and values.
255 : *
256 : * @param keys This key set stores keys that possibly contain special characters.
257 : *
258 : * @return A copy of the given KeySet containing only escaped keys
259 : */
260 46 : CppKeySet Coder::encodeKeySet (CppKeySet const & keys)
261 : {
262 46 : CppKeySet escaped{};
263 315 : for (auto key : keys)
264 : {
265 236 : CppKey encoded = encodeName (*key);
266 59 : encodeValue (encoded);
267 59 : escaped.append (encoded);
268 : }
269 46 : return escaped;
270 : }
271 :
272 : /**
273 : * @brief This function unescapes characters in key names and values.
274 : *
275 : * @param keys This key set stores keys that possibly contain escaped special characters.
276 : *
277 : * @return A copy of the given KeySet containing unescaped keys
278 : */
279 89 : CppKeySet Coder::decodeKeySet (CppKeySet const & keys)
280 : {
281 89 : CppKeySet unescaped{};
282 696 : for (auto key : keys)
283 : {
284 572 : CppKey decoded = decodeName (*key);
285 143 : decodeValue (decoded);
286 143 : unescaped.append (decoded);
287 : }
288 :
289 89 : return unescaped;
290 : }
291 :
292 : } // end namespace elektra
|