Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief Source for range plugin
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : *
8 : */
9 :
10 : #include "range.h"
11 : #include <ctype.h>
12 : #include <errno.h>
13 : #include <kdbassert.h>
14 : #include <kdberrors.h>
15 : #include <kdbhelper.h>
16 : #include <limits.h>
17 : #include <math.h>
18 : #include <stdio.h>
19 : #include <stdlib.h>
20 : #include <string.h>
21 : #include <strings.h>
22 :
23 :
24 : typedef enum
25 : {
26 : INT,
27 : UINT,
28 : FLOAT,
29 : CHAR,
30 : HEX,
31 : NA,
32 : } RangeType;
33 :
34 : typedef struct
35 : {
36 : RangeType type;
37 : union uValue
38 : {
39 : unsigned long long int i;
40 : long double f;
41 : } Value;
42 : } RangeValue;
43 :
44 :
45 : // switch min and max values if needed and apply -1 factor
46 146 : static void normalizeValues (RangeType type, RangeValue * min, RangeValue * max, RangeValue * a, RangeValue * b, int factorA, int factorB)
47 : {
48 146 : unsigned long long int tmpIA = factorA == -1 ? ULLONG_MAX - (*a).Value.i + 1 : (*a).Value.i;
49 146 : unsigned long long int tmpIB = factorB == -1 ? ULLONG_MAX - (*b).Value.i + 1 : (*b).Value.i;
50 146 : long double tmpFA = factorA * (*a).Value.f;
51 146 : long double tmpFB = factorB * (*b).Value.f;
52 146 : switch (type)
53 : {
54 : case INT:
55 : case HEX:
56 : case CHAR:
57 132 : if ((long long) tmpIA <= (long long) tmpIB)
58 : {
59 132 : min->Value.i = tmpIA;
60 132 : max->Value.i = tmpIB;
61 : }
62 : else
63 : {
64 0 : min->Value.i = tmpIB;
65 0 : max->Value.i = tmpIA;
66 : }
67 : break;
68 : case UINT:
69 8 : if (tmpIA <= tmpIB)
70 : {
71 8 : min->Value.i = tmpIA;
72 8 : max->Value.i = tmpIB;
73 : }
74 : else
75 : {
76 0 : min->Value.i = tmpIB;
77 0 : max->Value.i = tmpIA;
78 : }
79 : break;
80 : case FLOAT:
81 6 : if (tmpFA <= tmpFB)
82 : {
83 6 : min->Value.f = tmpFA;
84 6 : max->Value.f = tmpFB;
85 : }
86 : else
87 : {
88 0 : min->Value.f = tmpFB;
89 0 : max->Value.f = tmpFA;
90 : }
91 : break;
92 : default:
93 : break;
94 : }
95 146 : }
96 :
97 :
98 : // parse value starting at ptr and set ptr to the first character
99 : // after value. return a RangeValue with type == NA on error
100 :
101 250 : RangeValue strToValue (const char ** ptr, RangeType type)
102 : {
103 : RangeValue v;
104 250 : v.type = type;
105 250 : v.Value.i = 0;
106 250 : char * endPtr = NULL;
107 :
108 250 : errno = 0; // the c std library doesn't reset errno, so do it before conversions to be safe
109 250 : switch (type)
110 : {
111 : case INT:
112 : case UINT:
113 202 : v.Value.i = strtoull (*ptr, &endPtr, 10);
114 202 : if (errno == ERANGE || (errno != 0 && v.Value.i == 0))
115 : {
116 0 : v.type = NA;
117 : }
118 : break;
119 : case FLOAT:
120 12 : v.Value.f = strtold (*ptr, &endPtr);
121 12 : if (errno == ERANGE || (errno != 0 && fpclassify (v.Value.f) == FP_ZERO))
122 : {
123 0 : v.type = NA;
124 : }
125 : break;
126 : case HEX:
127 28 : v.Value.i = strtoull (*ptr, &endPtr, 16);
128 28 : if (errno == ERANGE || (errno != 0 && v.Value.i == 0))
129 : {
130 0 : v.type = NA;
131 : }
132 : break;
133 : case CHAR:
134 8 : if (!isalpha (**ptr))
135 : {
136 0 : v.type = NA;
137 : }
138 8 : v.Value.i = **ptr;
139 8 : endPtr = (char *) *ptr + 1;
140 : default:
141 : break;
142 : }
143 250 : *ptr = endPtr;
144 250 : return v;
145 : }
146 :
147 : // parse string into min - max values
148 : // return -1 on error, 0 on success;
149 :
150 152 : static int rangeStringToRange (const char * rangeString, RangeValue * min, RangeValue * max, RangeType type)
151 : {
152 152 : int factorA = 1; // multiplication factors for parsed values
153 152 : int factorB = 1; // if '-' is read, factor will be set to -1
154 : RangeValue a, b;
155 152 : a.type = type;
156 152 : b.type = type;
157 152 : a.Value.i = 0;
158 152 : b.Value.i = 0;
159 152 : int pos = 0;
160 :
161 152 : const char * ptr = rangeString;
162 782 : while (*ptr)
163 : {
164 484 : if (isspace (*ptr))
165 : {
166 82 : ++ptr;
167 82 : continue;
168 : }
169 402 : else if (*ptr == '-')
170 : {
171 152 : if (pos == 0)
172 : {
173 34 : if (factorA == -1)
174 : {
175 : return -1;
176 : }
177 32 : if (type == UINT)
178 : {
179 : return -1;
180 : }
181 : factorA = -1;
182 : }
183 118 : else if (pos == 1)
184 : {
185 : pos = 2;
186 : }
187 14 : else if (pos == 2)
188 : {
189 14 : if (factorB == -1)
190 : {
191 : return -1;
192 : }
193 12 : if (type == UINT)
194 : {
195 : return -1;
196 : }
197 : factorB = -1;
198 : }
199 : else
200 : {
201 : return -1;
202 : }
203 146 : ++ptr;
204 146 : continue;
205 : }
206 250 : else if (isalnum (*ptr))
207 : {
208 250 : if (pos == 0)
209 : {
210 148 : pos = 1;
211 148 : a = strToValue (&ptr, type);
212 : }
213 102 : else if (pos == 2)
214 : {
215 102 : pos = 3;
216 102 : b = strToValue (&ptr, type);
217 102 : if (b.type == NA)
218 : {
219 : return -1;
220 : }
221 : }
222 : else
223 : {
224 : return -1;
225 : }
226 : }
227 : else
228 : {
229 : return -1;
230 : }
231 : }
232 146 : if (pos != 1 && pos != 3)
233 : {
234 : return -1;
235 : }
236 146 : if (pos == 1)
237 : {
238 44 : b = a;
239 44 : factorB = factorA;
240 : }
241 146 : normalizeValues (type, min, max, &a, &b, factorA, factorB);
242 146 : return 0;
243 : }
244 :
245 :
246 152 : static int validateSingleRange (const char * valueStr, const char * rangeString, RangeType type)
247 : {
248 : RangeValue min, max;
249 152 : min.Value.i = 0;
250 152 : max.Value.i = 0;
251 152 : min.type = type;
252 152 : max.type = type;
253 152 : int rc = rangeStringToRange (rangeString, &min, &max, type);
254 152 : if (rc)
255 : {
256 : return -1;
257 : }
258 : RangeValue val;
259 146 : val.type = type;
260 146 : val.Value.i = 0;
261 : char * endPtr;
262 146 : errno = 0; // the c std library doesn't reset errno, so do it before conversions to be safe
263 146 : switch (type)
264 : {
265 : case INT:
266 : case UINT:
267 122 : val.Value.i = strtoull (valueStr, &endPtr, 10);
268 122 : break;
269 : case FLOAT:
270 6 : val.Value.f = strtold (valueStr, &endPtr);
271 6 : break;
272 : case HEX:
273 14 : val.Value.i = strtoull (valueStr, &endPtr, 16);
274 14 : break;
275 : case CHAR:
276 4 : val.Value.i = valueStr[0];
277 4 : break;
278 : default:
279 : break;
280 : }
281 146 : if (errno == ERANGE || (errno != 0 && val.Value.i == 0))
282 : {
283 : return -1;
284 : }
285 146 : switch (type)
286 : {
287 : case INT:
288 : case HEX:
289 : case CHAR:
290 132 : if ((long long) val.Value.i < (long long) min.Value.i || (long long) val.Value.i > (long long) max.Value.i)
291 : {
292 : return 0;
293 : }
294 : else
295 : {
296 59 : return 1;
297 : }
298 : break;
299 : case UINT:
300 8 : if (val.Value.i < min.Value.i || val.Value.i > max.Value.i)
301 : {
302 : return 0;
303 : }
304 : else
305 : {
306 8 : return 1;
307 : }
308 : break;
309 : case FLOAT:
310 6 : if (val.Value.f < min.Value.f || val.Value.f > max.Value.f)
311 : {
312 : return 0;
313 : }
314 : else
315 : {
316 4 : return 1;
317 : }
318 : break;
319 : default:
320 : return -1;
321 : break;
322 : }
323 : }
324 :
325 34 : static int validateMultipleRanges (const char * valueStr, const char * rangeString, Key * parentKey, RangeType type)
326 : {
327 34 : char * localCopy = elektraStrDup (rangeString);
328 34 : char * savePtr = NULL;
329 68 : char * token = strtok_r (localCopy, ",", &savePtr);
330 34 : int rc = validateSingleRange (valueStr, token, type);
331 34 : if (rc == 1)
332 : {
333 10 : elektraFree (localCopy);
334 10 : return 1;
335 : }
336 24 : else if (rc == -1)
337 : {
338 0 : elektraFree (localCopy);
339 0 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "Invalid syntax: %s", token);
340 0 : return -1;
341 : }
342 106 : while ((token = strtok_r (NULL, ",", &savePtr)) != NULL)
343 : {
344 44 : rc = validateSingleRange (valueStr, token, type);
345 44 : if (rc == 1)
346 : {
347 15 : elektraFree (localCopy);
348 15 : return 1;
349 : }
350 29 : else if (rc == -1)
351 : {
352 0 : elektraFree (localCopy);
353 0 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "Invalid syntax: %s", token);
354 0 : return -1;
355 : }
356 : }
357 9 : elektraFree (localCopy);
358 9 : return 0;
359 : }
360 :
361 112 : static RangeType stringToType (const Key * typeMeta)
362 : {
363 112 : if (typeMeta)
364 : {
365 : static const char * intTypes[] = {
366 : "short",
367 : "long",
368 : "long long",
369 : NULL,
370 : };
371 : static const char * uintTypes[] = {
372 : "unsigned short",
373 : "unsigned long",
374 : "unsigned long long",
375 : NULL,
376 : };
377 : static const char * floatTypes[] = {
378 : "float",
379 : "double",
380 : "long double",
381 : NULL,
382 : };
383 34 : const char * strVal = keyString (typeMeta);
384 136 : for (int i = 0; intTypes[i] != NULL; ++i)
385 : {
386 102 : if (!strcasecmp (strVal, intTypes[i])) return INT;
387 : }
388 74 : for (int i = 0; uintTypes[i] != NULL; ++i)
389 : {
390 88 : if (!strcasecmp (strVal, uintTypes[i])) return UINT;
391 : }
392 42 : for (int i = 0; floatTypes[i] != NULL; ++i)
393 : {
394 48 : if (!strcasecmp (strVal, floatTypes[i])) return FLOAT;
395 : }
396 14 : if (!strcasecmp (strVal, "char"))
397 : return CHAR;
398 10 : else if (!strcasecmp (strVal, "HEX"))
399 : return HEX;
400 : }
401 : return NA;
402 : }
403 :
404 112 : static RangeType getType (const Key * key)
405 : {
406 112 : const Key * typeMeta = keyGetMeta (key, "check/type");
407 112 : RangeType type = stringToType (typeMeta);
408 :
409 112 : if (type == NA)
410 : return INT;
411 : else
412 34 : return type;
413 : }
414 :
415 112 : static int validateKey (Key * key, Key * parentKey)
416 : {
417 112 : const Key * rangeMeta = keyGetMeta (key, "check/range");
418 112 : const char * rangeString = keyString (rangeMeta);
419 112 : RangeType type = getType (key);
420 112 : if (type == UINT)
421 : {
422 14 : const char * ptr = keyString (key);
423 28 : while (*ptr)
424 : {
425 14 : if (*ptr == '-')
426 : {
427 : return -1;
428 : }
429 10 : else if (isdigit (*ptr))
430 : {
431 : break;
432 : }
433 0 : ++ptr;
434 : }
435 : }
436 :
437 108 : if (!strchr (rangeString, ','))
438 : {
439 74 : int rc = validateSingleRange (keyString (key), rangeString, type);
440 74 : if (rc == -1)
441 : {
442 6 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "Invalid syntax: %s", keyString (rangeMeta));
443 6 : return -1;
444 : }
445 68 : else if (rc == 0)
446 : {
447 22 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (parentKey, "Value '%s' not within range %s", keyString (key), rangeString);
448 22 : return 0;
449 : }
450 : else
451 : {
452 : return rc;
453 : }
454 : }
455 : else
456 : {
457 34 : int rc = validateMultipleRanges (keyString (key), rangeString, parentKey, type);
458 34 : if (rc == 0)
459 : {
460 9 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (parentKey, "Value '%s' not within range %s", keyString (key), rangeString);
461 : }
462 : return rc;
463 : }
464 : }
465 :
466 47 : int elektraRangeGet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED)
467 : {
468 47 : if (!elektraStrCmp (keyName (parentKey), "system/elektra/modules/range"))
469 : {
470 32 : KeySet * contract =
471 32 : ksNew (30, keyNew ("system/elektra/modules/range", KEY_VALUE, "range plugin waits for your orders", KEY_END),
472 : keyNew ("system/elektra/modules/range/exports", KEY_END),
473 : keyNew ("system/elektra/modules/range/exports/get", KEY_FUNC, elektraRangeGet, KEY_END),
474 : keyNew ("system/elektra/modules/range/exports/set", KEY_FUNC, elektraRangeSet, KEY_END),
475 : keyNew ("system/elektra/modules/range/exports/validateKey", KEY_FUNC, validateKey, KEY_END),
476 : #include ELEKTRA_README
477 : keyNew ("system/elektra/modules/range/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END);
478 32 : ksAppend (returned, contract);
479 32 : ksDel (contract);
480 :
481 32 : return 1; // success
482 : }
483 : // get all keys
484 :
485 : return 1; // success
486 : }
487 :
488 113 : int elektraRangeSet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED)
489 : {
490 : // set all keys
491 : // this function is optional
492 : Key * cur;
493 298 : while ((cur = ksNext (returned)) != NULL)
494 : {
495 113 : const Key * meta = keyGetMeta (cur, "check/range");
496 113 : if (meta)
497 : {
498 112 : int rc = validateKey (cur, parentKey);
499 112 : if (rc <= 0)
500 : {
501 : return -1;
502 : }
503 : }
504 : }
505 : return 1; // success
506 : }
507 :
508 176 : Plugin * ELEKTRA_PLUGIN_EXPORT
509 : {
510 : // clang-format off
511 176 : return elektraPluginExport ("range",
512 : ELEKTRA_PLUGIN_GET, &elektraRangeGet,
513 : ELEKTRA_PLUGIN_SET, &elektraRangeSet,
514 : ELEKTRA_PLUGIN_END);
515 : }
516 :
|