Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief Source for hexnumber plugin
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : *
8 : */
9 :
10 : #include "hexnumber.h"
11 :
12 : #include <kdbease.h>
13 : #include <kdberrors.h>
14 : #include <kdbhelper.h>
15 : #include <stdbool.h>
16 : #include <stdio.h>
17 : #include <stdlib.h>
18 :
19 : typedef struct
20 : {
21 : bool forceConversion;
22 : KeySet * integerTypes;
23 : } HexnumberData;
24 :
25 : /**
26 : * Creates a KeySet representing the contract of this plugin.
27 : */
28 37 : static KeySet * elektraContract (void)
29 : {
30 37 : return ksNew (
31 : 30,
32 : keyNew ("system/elektra/modules/" ELEKTRA_HEXNUMBER_PLUGIN_NAME, KEY_VALUE, "hexnumber plugin waits for your orders",
33 : KEY_END),
34 : keyNew ("system/elektra/modules/" ELEKTRA_HEXNUMBER_PLUGIN_NAME "/exports", KEY_END),
35 : keyNew ("system/elektra/modules/" ELEKTRA_HEXNUMBER_PLUGIN_NAME "/exports/get", KEY_FUNC, elektraHexnumberGet, KEY_END),
36 : keyNew ("system/elektra/modules/" ELEKTRA_HEXNUMBER_PLUGIN_NAME "/exports/set", KEY_FUNC, elektraHexnumberSet, KEY_END),
37 : keyNew ("system/elektra/modules/" ELEKTRA_HEXNUMBER_PLUGIN_NAME "/exports/close", KEY_FUNC, elektraHexnumberClose, KEY_END),
38 :
39 : #include ELEKTRA_README
40 :
41 : keyNew ("system/elektra/modules/" ELEKTRA_HEXNUMBER_PLUGIN_NAME "/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END),
42 : KS_END);
43 : }
44 :
45 : /**
46 : * Converts a Key with a hexadecimal number value to a Key with a decimal value.
47 : *
48 : * @pre The key has to actually contain a hexadecimal number string.
49 : *
50 : * @param key The Key which should be converted.
51 : * @param parentKey The parent Key used to set errors.
52 : *
53 : * @retval #ELEKTRA_PLUGIN_STATUS_SUCCESS if the Key was successfully converted
54 : * @retval #ELEKTRA_PLUGIN_STATUS_ERROR if the Key could not be converted
55 : */
56 41 : static int convertHexToDec (Key * key, Key * parentKey)
57 : {
58 : // get hex string from key
59 41 : const char * hexValue = keyString (key);
60 :
61 41 : int errnoSaved = errno;
62 :
63 : // TODO: use supermacro from PR #1850 once merged
64 : // convert hex string to long long int
65 41 : errno = 0;
66 : char * endPtr;
67 41 : unsigned long long int value = strtoull (hexValue, &endPtr, 16);
68 41 : if (errno == ERANGE && value == ULLONG_MAX)
69 : {
70 0 : errno = errnoSaved;
71 0 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (parentKey, "Hexadecimal number %s out of range 0 to %llu", hexValue, ULLONG_MAX);
72 0 : return ELEKTRA_PLUGIN_STATUS_ERROR;
73 : }
74 41 : else if ((errno != 0 && value == 0) || endPtr == hexValue || *endPtr != '\0')
75 : {
76 0 : errno = errnoSaved;
77 0 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "Hexadecimal number '%s' could not be read", hexValue);
78 0 : return ELEKTRA_PLUGIN_STATUS_ERROR;
79 : }
80 41 : errno = errnoSaved;
81 :
82 : // convert long long int back to string (formatted as decimal)
83 41 : int result = snprintf (NULL, 0, "%llu", value);
84 41 : if (result < 0)
85 : {
86 0 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "Unable to convert '%s' into decimal", hexValue);
87 0 : return ELEKTRA_PLUGIN_STATUS_ERROR;
88 : }
89 :
90 41 : const size_t length = (size_t) result + 1;
91 41 : char * decValue = elektraMalloc (length);
92 41 : if (!decValue)
93 : {
94 0 : ELEKTRA_MALLOC_ERROR (parentKey, length);
95 0 : return ELEKTRA_PLUGIN_STATUS_ERROR;
96 : }
97 :
98 41 : result = snprintf (decValue, length, "%llu", value);
99 41 : if (result < 0)
100 : {
101 0 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "Unable to convert '%s' into decimal", hexValue);
102 0 : elektraFree (decValue);
103 0 : return ELEKTRA_PLUGIN_STATUS_ERROR;
104 : }
105 :
106 : // set decimal string in key and set internal metadata
107 41 : keySetString (key, decValue);
108 41 : keySetMeta (key, ELEKTRA_HEXNUMBER_META_KEY, "1");
109 :
110 41 : elektraFree (decValue);
111 :
112 41 : return ELEKTRA_PLUGIN_STATUS_SUCCESS;
113 : }
114 :
115 : /**
116 : * Converts a Key with a decimal number value to a Key with a hexadecimal value.
117 : *
118 : * @pre The key has to actually contain a decimal number string.
119 : *
120 : * @param key The Key whose value should be converted.
121 : * @param parentKey The parent Key used to set errors.
122 : *
123 : * @retval #ELEKTRA_PLUGIN_STATUS_SUCCESS if the Key was successfully converted
124 : * @retval #ELEKTRA_PLUGIN_STATUS_ERROR if the Key could not be converted
125 : */
126 6 : static int convertDecToHex (Key * key, Key * parentKey)
127 : {
128 : // get decimal string from key
129 6 : const char * decValue = keyString (key);
130 :
131 6 : int errnoSaved = errno;
132 :
133 : // convert hex string to long long int
134 6 : errno = 0;
135 : char * endPtr;
136 6 : unsigned long long int value = strtoull (decValue, &endPtr, 10);
137 6 : if (errno == ERANGE && value == ULLONG_MAX)
138 : {
139 0 : errno = errnoSaved;
140 0 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (parentKey, "Decimal number %s out of range 0 to %llu", decValue, ULLONG_MAX);
141 0 : return ELEKTRA_PLUGIN_STATUS_ERROR;
142 : }
143 6 : else if ((errno != 0 && value == 0) || endPtr == decValue)
144 : {
145 0 : errno = errnoSaved;
146 0 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "Decimal number '%s' could not be read", decValue);
147 0 : return ELEKTRA_PLUGIN_STATUS_ERROR;
148 : }
149 6 : errno = errnoSaved;
150 :
151 : // convert long long int back to string (formatted as hexadecimal)
152 6 : const int result = snprintf (NULL, 0, "0x%llx", value);
153 6 : if (result < 0)
154 : {
155 0 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "Unable to convert '%s' into hexadecimal", decValue);
156 0 : return ELEKTRA_PLUGIN_STATUS_ERROR;
157 : }
158 :
159 6 : const size_t length = (size_t) result + 1;
160 6 : char * hexValue = elektraMalloc (length);
161 6 : if (!hexValue)
162 : {
163 0 : ELEKTRA_MALLOC_ERROR (parentKey, length);
164 0 : return ELEKTRA_PLUGIN_STATUS_ERROR;
165 : }
166 :
167 6 : if (snprintf (hexValue, length, "0x%llx", value) < 0)
168 : {
169 0 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "Unable to convert '%s' into hexadecimal", decValue);
170 0 : elektraFree (hexValue);
171 0 : return ELEKTRA_PLUGIN_STATUS_ERROR;
172 : }
173 :
174 : // set hex string in key and unset internal metadata
175 6 : keySetString (key, hexValue);
176 6 : keySetMeta (key, ELEKTRA_HEXNUMBER_META_KEY, "0");
177 :
178 :
179 6 : elektraFree (hexValue);
180 :
181 6 : return ELEKTRA_PLUGIN_STATUS_SUCCESS;
182 : }
183 :
184 : /**
185 : * Checks whether a given Key's value is a hexadecimal string.
186 : *
187 : * @param key The Key that should be checked.
188 : *
189 : * @retval #true if the Key's value starts with 0x
190 : * @retval #false otherwise
191 : */
192 66 : static bool isHexString (const Key * key)
193 : {
194 66 : return elektraStrNCaseCmp (keyString (key), "0x", 2) == 0;
195 : }
196 :
197 : /**
198 : * Checks whether a given Key is specified to have a hexadecimal base.
199 : *
200 : * @param key The Key that should be checked.
201 : *
202 : * @retval #true if the Key's metadata contains the key /unit/base and its value is hex
203 : * @retval #false otherwise
204 : */
205 66 : static bool isHexUnitBase (const Key * key)
206 : {
207 66 : const Key * unitBaseMeta = keyGetMeta (key, "unit/base");
208 66 : return elektraStrCmp (keyString (unitBaseMeta), "hex") == 0;
209 : }
210 :
211 : /**
212 : * Filter function used in hasType() for call to elektraKsFilter()
213 : */
214 205 : static int __hasTypeFilter (const Key * key, void * argument)
215 : {
216 205 : const char * type = (const char *) argument;
217 205 : return keyIsString (key) && strcmp (type, keyString (key)) == 0;
218 : }
219 :
220 : /**
221 : * Checks whether a given key has one of the given types.
222 : *
223 : * @param key The Key that should be checked.
224 : * @param types An array of strings containing possible values for the /type metadata.
225 : * @param typeCount The number of strings in the @p types array.
226 : *
227 : * @retval #true if the Key's type metadata is contained in the types list
228 : * @retval #false otherwise
229 : */
230 62 : static bool hasType (const Key * key, KeySet * types)
231 : {
232 62 : const Key * typeMeta = keyGetMeta (key, "type");
233 62 : if (!typeMeta || !types)
234 : {
235 : return false;
236 : }
237 :
238 58 : const char * type = keyString (typeMeta);
239 58 : KeySet * res = ksNew ((size_t) ksGetSize (types), KS_END);
240 58 : elektraKsFilter (res, types, &__hasTypeFilter, (void *) type);
241 :
242 58 : const ssize_t size = ksGetSize (res);
243 58 : ksDel (res);
244 :
245 58 : return size > 0;
246 : }
247 :
248 : /**
249 : * Checks whether a given Key's type metadata is #ELEKTRA_HEXNUMBER_META_TYPE.
250 : *
251 : * @param key The Key that should be checked.
252 : *
253 : * @retval #true if the Key's type metadata is #ELEKTRA_HEXNUMBER_META_TYPE
254 : * @retval #false otherwise
255 : */
256 20 : static bool hasHexType (const Key * key)
257 : {
258 20 : const Key * typeMeta = keyGetMeta (key, ELEKTRA_HEXNUMBER_META_KEY);
259 20 : return typeMeta && elektraStrCmp (keyString (typeMeta), "1") == 0;
260 : }
261 :
262 : /**
263 : * Parse the configuration as described in README.md
264 : * @param config the configuration KeySet provided by elektraPluginGetConfig
265 : * @param data pointer to the struct to store the configuration in
266 : * @param errorKey Key used to store error information
267 : */
268 23 : int parseConfig (KeySet * config, HexnumberData * data, Key * errorKey)
269 : {
270 23 : Key * forceKey = ksLookupByName (config, "/force", 0);
271 23 : if (forceKey)
272 : {
273 2 : data->forceConversion = true;
274 : }
275 :
276 23 : Key * typesKey = keyNew ("/accept/type", KEY_END);
277 23 : KeySet * types = elektraArrayGet (typesKey, config);
278 23 : keyDel (typesKey);
279 :
280 23 : if (!types)
281 : {
282 0 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERROR (errorKey, "Could not parse config. Types not set correctly");
283 0 : return ELEKTRA_PLUGIN_STATUS_ERROR;
284 : }
285 :
286 23 : data->integerTypes = types;
287 :
288 23 : return ELEKTRA_PLUGIN_STATUS_SUCCESS;
289 : }
290 :
291 : /**
292 : * Establish the plugin contract and convert all hexadecimal values in the KeySet to decimal.
293 : *
294 : * @param handle This parameter stores the configuration of the plugin.
295 : * @param returned This parameter specifies the key set that this function updates.
296 : * @param parentKey The function stores information about errors/warnings in this parameter.
297 : *
298 : * @retval #ELEKTRA_PLUGIN_STATUS_SUCCESS if any keys were updated
299 : * @retval #ELEKTRA_PLUGIN_STATUS_NO_UPDATE if \p returned was not modified
300 : * @retval #ELEKTRA_PLUGIN_STATUS_ERROR on failure
301 : */
302 59 : int elektraHexnumberGet (Plugin * handle, KeySet * returned, Key * parentKey)
303 : {
304 59 : if (!elektraStrCmp (keyName (parentKey), "system/elektra/modules/" ELEKTRA_HEXNUMBER_PLUGIN_NAME))
305 : {
306 37 : KeySet * contract = elektraContract ();
307 37 : ksAppend (returned, contract);
308 37 : ksDel (contract);
309 :
310 37 : return ELEKTRA_PLUGIN_STATUS_SUCCESS;
311 : }
312 :
313 22 : HexnumberData * data = elektraPluginGetData (handle);
314 22 : if (!data)
315 : {
316 22 : KeySet * config = elektraPluginGetConfig (handle);
317 22 : data = elektraCalloc (sizeof (HexnumberData));
318 22 : if (parseConfig (config, data, parentKey) == ELEKTRA_PLUGIN_STATUS_ERROR)
319 : {
320 0 : elektraFree (data);
321 0 : return ELEKTRA_PLUGIN_STATUS_ERROR;
322 : }
323 22 : elektraPluginSetData (handle, data);
324 : }
325 :
326 : Key * cur;
327 22 : ksRewind (returned);
328 :
329 22 : KeySet * defaultIntegerTypes = ksNew (7, keyNew ("system/accept/type/#0", KEY_VALUE, "byte", KEY_END),
330 : keyNew ("system/accept/type/#1", KEY_VALUE, "short", KEY_END),
331 : keyNew ("system/accept/type/#2", KEY_VALUE, "long", KEY_END),
332 : keyNew ("system/accept/type/#3", KEY_VALUE, "long_long", KEY_END),
333 : keyNew ("system/accept/type/#4", KEY_VALUE, "unsigned_short", KEY_END),
334 : keyNew ("system/accept/type/#5", KEY_VALUE, "unsigned_long", KEY_END),
335 : keyNew ("system/accept/type/#6", KEY_VALUE, "unsigned_long_long", KEY_END), KS_END);
336 :
337 22 : int status = ELEKTRA_PLUGIN_STATUS_NO_UPDATE;
338 110 : while ((cur = ksNext (returned)) != NULL)
339 : {
340 66 : if (!keyIsString (cur))
341 : {
342 0 : continue;
343 : }
344 :
345 66 : bool hexString = isHexString (cur);
346 66 : if (isHexUnitBase (cur))
347 : {
348 6 : if (hexString)
349 : {
350 6 : status |= convertHexToDec (cur, parentKey);
351 : }
352 : else
353 : {
354 0 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (
355 : parentKey, "Key '%s' has unit/base metadata set as hex but value '%s' does not start with 0x",
356 : keyName (cur), keyString (cur));
357 0 : status |= ELEKTRA_PLUGIN_STATUS_ERROR;
358 : }
359 : }
360 60 : else if (hexString && (data->forceConversion || hasType (cur, data->integerTypes) || hasType (cur, defaultIntegerTypes)))
361 : {
362 35 : status |= convertHexToDec (cur, parentKey);
363 : }
364 : }
365 :
366 22 : ksDel (defaultIntegerTypes);
367 :
368 22 : return status;
369 : }
370 :
371 : /**
372 : * Convert all values in the KeySet originally stored as hexadecimal (marked by type metdata) to hexadecimal.
373 : *
374 : * @param handle This parameter stores the configuration of the plugin.
375 : * @param returned This parameter specifies the key set that this function updates.
376 : * @param parentKey The function stores information about errors/warnings in this parameter.
377 : *
378 : * @retval #ELEKTRA_PLUGIN_STATUS_SUCCESS if any keys were updated
379 : * @retval #ELEKTRA_PLUGIN_STATUS_NO_UPDATE if \p returned was not modified
380 : * @retval #ELEKTRA_PLUGIN_STATUS_ERROR on failure
381 : */
382 10 : int elektraHexnumberSet (Plugin * handle, KeySet * returned, Key * parentKey)
383 : {
384 10 : HexnumberData * data = elektraPluginGetData (handle);
385 10 : if (!data)
386 : {
387 1 : KeySet * config = elektraPluginGetConfig (handle);
388 1 : data = elektraCalloc (sizeof (HexnumberData));
389 1 : if (parseConfig (config, data, parentKey) == ELEKTRA_PLUGIN_STATUS_ERROR)
390 : {
391 0 : elektraFree (data);
392 0 : return ELEKTRA_PLUGIN_STATUS_ERROR;
393 : }
394 1 : elektraPluginSetData (handle, data);
395 : }
396 :
397 : Key * cur;
398 10 : ksRewind (returned);
399 :
400 10 : int status = ELEKTRA_PLUGIN_STATUS_NO_UPDATE;
401 40 : while ((cur = ksNext (returned)) != NULL)
402 : {
403 20 : if (keyIsString (cur) && hasHexType (cur))
404 : {
405 6 : status |= convertDecToHex (cur, parentKey);
406 : }
407 : }
408 :
409 : return status;
410 : }
411 :
412 103 : int elektraHexnumberClose (Plugin * handle, Key * parentKey ELEKTRA_UNUSED)
413 : {
414 103 : HexnumberData * data = elektraPluginGetData (handle);
415 103 : if (data)
416 : {
417 23 : ksDel (data->integerTypes);
418 23 : elektraFree (data);
419 23 : elektraPluginSetData (handle, NULL);
420 : }
421 :
422 103 : return ELEKTRA_PLUGIN_STATUS_SUCCESS;
423 : }
424 :
425 : /**
426 : * Exports the plugin to be used by Elektra.
427 : */
428 103 : Plugin * ELEKTRA_PLUGIN_EXPORT
429 : {
430 : // clang-format off
431 103 : return elektraPluginExport (ELEKTRA_HEXNUMBER_PLUGIN_NAME,
432 : ELEKTRA_PLUGIN_GET, &elektraHexnumberGet,
433 : ELEKTRA_PLUGIN_SET, &elektraHexnumberSet,
434 : ELEKTRA_PLUGIN_CLOSE, &elektraHexnumberClose,
435 : ELEKTRA_PLUGIN_END);
436 : }
|