Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief Source for conditionals plugin
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : *
8 : */
9 :
10 :
11 : #ifndef HAVE_KDBCONFIG
12 : #include "kdbconfig.h"
13 : #endif
14 :
15 : #include <ctype.h>
16 : #include <errno.h>
17 : #include <kdbease.h>
18 : #include <kdberrors.h>
19 : #include <kdbmeta.h>
20 : #include <kdbproposal.h> //keyRel2
21 : #include <math.h>
22 : #include <regex.h>
23 : #include <stdio.h>
24 : #include <stdlib.h>
25 : #include <string.h>
26 : #include <sys/types.h>
27 :
28 : #include "conditionals.h"
29 :
30 : #define EPSILON 0.00001
31 :
32 : #define REGEX_FLAGS_CONDITION (REG_EXTENDED)
33 :
34 : typedef enum
35 : {
36 : EQU,
37 : NOT,
38 : LT,
39 : LE,
40 : GT,
41 : GE,
42 : SET,
43 : NEX,
44 : AND,
45 : OR,
46 : } Comparator;
47 :
48 : typedef enum
49 : {
50 : CONDITION,
51 : ASSIGN
52 : } Operation;
53 :
54 : typedef enum
55 : {
56 : TRUE = 1,
57 : FALSE = 0,
58 : ERROR = -1,
59 : NOEXPR = -3,
60 : } CondResult;
61 :
62 120 : static int isValidSuffix (char * suffix, const Key * suffixList)
63 : {
64 120 : if (!suffixList) return 0;
65 8 : char * searchString = elektraMalloc (strlen (suffix) + 3);
66 8 : snprintf (searchString, strlen (suffix) + 3, "'%s'", suffix);
67 8 : int ret = 0;
68 8 : if (strstr (keyString (suffixList), searchString))
69 : {
70 8 : ret = 1;
71 : }
72 8 : elektraFree (searchString);
73 8 : return ret;
74 : }
75 :
76 228 : static int isNumber (const char * s, const Key * suffixList)
77 : {
78 228 : char * endPtr = NULL;
79 : int ret;
80 228 : ret = (int) strtol (s, &endPtr, 10);
81 228 : if (*endPtr != 0 && isValidSuffix (endPtr, suffixList))
82 : {
83 : return 1;
84 : }
85 220 : if (*endPtr != 0)
86 : {
87 : return 0;
88 : }
89 108 : else if (ret == 0 && errno == EINVAL)
90 : {
91 : return 0;
92 : }
93 108 : else if (*endPtr == '.')
94 : {
95 0 : ret = (int) strtof (s, &endPtr);
96 0 : if (*endPtr != 0 && isValidSuffix (endPtr, suffixList))
97 : {
98 : return 2;
99 : }
100 0 : if (*endPtr != 0)
101 : {
102 : return 0;
103 : }
104 0 : else if (ret == 0 && errno == EINVAL)
105 : {
106 : return 0;
107 : }
108 : else
109 : {
110 0 : return 2;
111 : }
112 : }
113 : else
114 : {
115 : return 1;
116 : }
117 : }
118 182 : static int compareStrings (const char * s1, const char * s2, const Key * suffixList)
119 : {
120 : int ret;
121 : int ret2;
122 : float result;
123 182 : int retval = -1;
124 182 : if (s1 == NULL)
125 : {
126 : retval = -1;
127 : }
128 182 : else if (s2 == NULL)
129 : {
130 : retval = 1;
131 : }
132 182 : else if (*s1 == '\0' && *s2 != '\0')
133 : {
134 : retval = -1;
135 : }
136 178 : else if (*s2 == '\0' && *s1 != '\0')
137 : {
138 : retval = 1;
139 : }
140 176 : else if (*s1 == '\0' && *s2 == '\0')
141 : {
142 : retval = 0;
143 : }
144 170 : else if ((ret = isNumber (s1, suffixList)) && (ret2 = isNumber (s2, suffixList)))
145 58 : {
146 : char * s1EndPtr;
147 : char * s2EndPtr;
148 58 : if (ret == 2 || ret2 == 2)
149 : {
150 0 : float s1Value = strtof (s1, &s1EndPtr);
151 0 : float s2Value = strtof (s2, &s2EndPtr);
152 0 : if (!strcmp (s1EndPtr, s2EndPtr) || *s1EndPtr == 0 || *s2EndPtr == 0)
153 : {
154 0 : result = (float) fabs (s1Value - s2Value);
155 0 : if (result < EPSILON)
156 : {
157 : retval = 0;
158 : }
159 : else
160 : {
161 0 : retval = 1;
162 : }
163 : }
164 : else
165 : {
166 0 : retval = strcmp (s1, s2);
167 : }
168 : }
169 : else
170 : {
171 58 : int s1Value = (int) strtol (s1, &s1EndPtr, 10);
172 58 : int s2Value = (int) strtol (s2, &s2EndPtr, 10);
173 58 : if (!strcmp (s1EndPtr, s2EndPtr) || *s1EndPtr == 0 || *s2EndPtr == 0)
174 : {
175 58 : retval = (int) (s1Value - s2Value);
176 : }
177 : else
178 : {
179 0 : retval = strcmp (s1, s2);
180 : }
181 : }
182 : }
183 : else
184 : {
185 112 : retval = strcmp (s1, s2);
186 : }
187 182 : return retval;
188 : }
189 :
190 186 : static CondResult evalCondition (const Key * curKey, const char * leftSide, Comparator cmpOp, const char * rightSide,
191 : const char * condition, const Key * suffixList, KeySet * ks, Key * parentKey)
192 : {
193 186 : char * lookupName = NULL;
194 186 : char * compareTo = NULL;
195 : Key * key;
196 : int len;
197 186 : long result = 0;
198 186 : if (rightSide)
199 : {
200 182 : if (rightSide[0] == '\'')
201 : {
202 : // right side of the statement is a literal enclosed by ''
203 164 : char * endPos = strchr (rightSide + 1, '\'');
204 164 : if (!endPos)
205 : {
206 : result = ERROR;
207 : goto Cleanup;
208 : }
209 164 : if (elektraRealloc ((void **) &compareTo, (size_t) (endPos - rightSide)) < 0)
210 : {
211 0 : ELEKTRA_SET_OUT_OF_MEMORY_ERROR (parentKey, "Out of memory");
212 0 : result = ERROR;
213 0 : goto Cleanup;
214 : }
215 164 : memset (compareTo, 0, (size_t) (endPos - rightSide));
216 164 : strncat (compareTo, rightSide + 1, (size_t) (endPos - rightSide - 1));
217 : }
218 18 : else if (rightSide && elektraStrLen (rightSide) > 1)
219 : {
220 : // not a literal, it has to be a key
221 18 : if (rightSide[0] == '@')
222 2 : len = (int) ((size_t) keyGetNameSize (parentKey) + elektraStrLen (rightSide));
223 16 : else if (!strncmp (rightSide, "..", 2) || (rightSide[0] == '.'))
224 16 : len = (int) ((size_t) keyGetNameSize (curKey) + elektraStrLen (rightSide));
225 : else
226 0 : len = (int) elektraStrLen (rightSide);
227 :
228 18 : if (elektraRealloc ((void **) &lookupName, (size_t) len) < 0)
229 : {
230 0 : ELEKTRA_SET_OUT_OF_MEMORY_ERROR (parentKey, "Out of memory");
231 0 : result = ERROR;
232 0 : goto Cleanup;
233 : }
234 18 : if (rightSide[0] == '@')
235 2 : snprintf (lookupName, (size_t) len, "%s/%s", keyName (parentKey), rightSide + 1);
236 16 : else if (rightSide[0] == '.') // either starts with . or .., doesn't matter at this point
237 16 : snprintf (lookupName, (size_t) len, "%s/%s", keyName (curKey), rightSide);
238 : else
239 0 : snprintf (lookupName, (size_t) len, "%s", rightSide);
240 :
241 18 : key = ksLookupByName (ks, lookupName, 0);
242 18 : if (!key)
243 : {
244 0 : if (!keyGetMeta (parentKey, "error"))
245 : {
246 0 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (parentKey,
247 : "Key %s not found but is required for the evaluation of %s",
248 : lookupName, condition);
249 : }
250 : result = FALSE;
251 : goto Cleanup;
252 : }
253 18 : if (elektraRealloc ((void **) &compareTo, (size_t) keyGetValueSize (key)) < 0)
254 : {
255 0 : ELEKTRA_SET_OUT_OF_MEMORY_ERROR (parentKey, "Out of memory");
256 0 : result = ERROR;
257 0 : goto Cleanup;
258 : }
259 18 : strcpy (compareTo, keyString (key));
260 : }
261 : }
262 186 : if (leftSide[0] == '@')
263 0 : len = (int) ((size_t) keyGetNameSize (parentKey) + elektraStrLen (leftSide));
264 186 : else if (!strncmp (leftSide, "..", 2) || (leftSide[0] == '.'))
265 170 : len = (int) ((size_t) keyGetNameSize (curKey) + elektraStrLen (leftSide));
266 : else
267 16 : len = (int) elektraStrLen (leftSide);
268 :
269 186 : if (elektraRealloc ((void **) &lookupName, (size_t) len) < 0)
270 : {
271 0 : ELEKTRA_SET_OUT_OF_MEMORY_ERROR (parentKey, "Out of memory");
272 0 : result = ERROR;
273 0 : goto Cleanup;
274 : }
275 186 : if (leftSide[0] == '@')
276 0 : snprintf (lookupName, (size_t) len, "%s/%s", keyName (parentKey), leftSide + 1);
277 186 : else if (leftSide[0] == '.') // either . or .., doesn't matter here
278 170 : snprintf (lookupName, (size_t) len, "%s/%s", keyName (curKey), leftSide);
279 : else
280 16 : snprintf (lookupName, (size_t) len, "%s", leftSide);
281 186 : key = ksLookupByName (ks, lookupName, 0);
282 186 : if (cmpOp == NEX)
283 : {
284 4 : if (key)
285 : result = FALSE;
286 : else
287 2 : result = TRUE;
288 : goto Cleanup;
289 : }
290 182 : if (!key && cmpOp != OR && cmpOp != AND)
291 : {
292 0 : if (!keyGetMeta (parentKey, "error"))
293 : {
294 0 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (parentKey, "Key %s not found but is required for the evaluation of %s",
295 : lookupName, condition);
296 : }
297 : result = FALSE;
298 : goto Cleanup;
299 : }
300 : long ret;
301 182 : if (cmpOp == OR || cmpOp == AND)
302 16 : ret = compareStrings (leftSide, rightSide, NULL);
303 : else
304 166 : ret = compareStrings (keyString (key), compareTo, suffixList);
305 182 : switch (cmpOp)
306 : {
307 : case EQU:
308 128 : if (!ret) result = TRUE;
309 : break;
310 : case NOT:
311 4 : if (ret) result = TRUE;
312 : break;
313 : case LT:
314 16 : if (ret < 0) result = TRUE;
315 : break;
316 : case LE:
317 2 : if (ret <= 0) result = TRUE;
318 : break;
319 : case GT:
320 4 : if (ret > 0) result = TRUE;
321 : break;
322 : case GE:
323 8 : if (ret >= 0) result = TRUE;
324 : break;
325 : case SET:
326 4 : keySetString (key, compareTo);
327 4 : result = TRUE;
328 4 : break;
329 : case AND:
330 6 : if (ret == 0 && !strcmp (leftSide, "'1'")) result = TRUE;
331 : break;
332 : case OR:
333 10 : if (!strcmp (leftSide, "'1'") || (rightSide && !strcmp (rightSide, "'1'"))) result = TRUE;
334 : break;
335 : default:
336 : result = ERROR;
337 : break;
338 : }
339 : // freeing allocated heap
340 : Cleanup:
341 186 : if (lookupName) elektraFree (lookupName);
342 186 : if (compareTo) elektraFree (compareTo);
343 186 : return (CondResult) result;
344 : }
345 :
346 :
347 186 : static char * condition2cmpOp (const char * condition, Comparator * cmpOp)
348 : {
349 : char * opStr;
350 186 : if ((opStr = strstr (condition, "==")))
351 : {
352 128 : *cmpOp = EQU;
353 : }
354 58 : else if ((opStr = strstr (condition, "!=")))
355 : {
356 4 : *cmpOp = NOT;
357 : }
358 54 : else if ((opStr = strstr (condition, "<=")))
359 : {
360 2 : *cmpOp = LE;
361 : }
362 52 : else if ((opStr = strstr (condition, "<")))
363 : {
364 16 : *cmpOp = LT;
365 : }
366 36 : else if ((opStr = strstr (condition, ">=")))
367 : {
368 8 : *cmpOp = GE;
369 : }
370 28 : else if ((opStr = strstr (condition, ">")))
371 : {
372 4 : *cmpOp = GT;
373 : }
374 24 : else if ((opStr = strstr (condition, ":=")))
375 : {
376 4 : *cmpOp = SET;
377 : }
378 20 : else if ((opStr = strstr (condition, "&&")))
379 : {
380 6 : *cmpOp = AND;
381 : }
382 14 : else if ((opStr = strstr (condition, "||")))
383 : {
384 10 : *cmpOp = OR;
385 : }
386 : else
387 : {
388 : char * ptr = (char *) condition;
389 4 : while (isspace (*ptr) && *ptr != '!' && *ptr)
390 : {
391 0 : ++ptr;
392 : }
393 4 : if (*ptr != '!')
394 : {
395 : return NULL;
396 : }
397 : else
398 : {
399 4 : opStr = ptr + strlen (condition) + 1;
400 4 : *cmpOp = NEX;
401 : }
402 : }
403 : return opStr;
404 : }
405 :
406 186 : static CondResult parseSingleCondition (const Key * key, const char * condition, const Key * suffixList, KeySet * ks, Key * parentKey)
407 : {
408 : Comparator cmpOp;
409 : char * opStr;
410 186 : opStr = condition2cmpOp (condition, &cmpOp);
411 :
412 186 : if (!opStr)
413 : {
414 : return ERROR;
415 : }
416 :
417 : size_t opLen;
418 186 : if (cmpOp == LT || cmpOp == GT || cmpOp == NEX)
419 : {
420 : opLen = 1;
421 : }
422 : else
423 : {
424 162 : opLen = 2;
425 : }
426 186 : unsigned long startPos = 0;
427 186 : unsigned long endPos = 0;
428 186 : char * ptr = (char *) condition;
429 186 : int firstNot = 1;
430 186 : if (*ptr == '!')
431 : {
432 4 : ++ptr;
433 4 : ++startPos;
434 4 : firstNot = 0;
435 : }
436 210 : while (isspace (*ptr))
437 : {
438 24 : ++ptr;
439 24 : if ((cmpOp == NEX) && (*ptr == '!') && firstNot)
440 : {
441 0 : firstNot = 0;
442 0 : ++ptr;
443 0 : ++startPos;
444 : }
445 24 : ++startPos;
446 : }
447 :
448 186 : ptr = opStr - 1;
449 868 : while (ptr > condition && isspace (*ptr))
450 : {
451 496 : --ptr;
452 496 : ++endPos;
453 : }
454 186 : int len = (int) ((unsigned long) (opStr - condition) - endPos - startPos + 2);
455 186 : char * leftSide = elektraMalloc ((size_t) len);
456 186 : char * rightSide = NULL;
457 186 : strncpy (leftSide, condition + startPos, (size_t) (len - 2));
458 186 : leftSide[len - 2] = '\0';
459 186 : startPos = 0;
460 186 : endPos = 0;
461 186 : if (cmpOp == NEX)
462 : {
463 : goto parseSingleEnd;
464 : }
465 182 : ptr = opStr + opLen;
466 492 : while (isspace (*ptr))
467 : {
468 128 : ++ptr;
469 128 : ++startPos;
470 : }
471 182 : ptr = (char *) condition + (elektraStrLen (condition) - 2);
472 998 : while (isspace (*ptr))
473 : {
474 634 : --ptr;
475 634 : ++endPos;
476 : }
477 182 : len = (int) (elektraStrLen (condition) - (unsigned long) (opStr - condition) - opLen - endPos - startPos);
478 182 : rightSide = elektraMalloc ((size_t) len);
479 182 : strncpy (rightSide, opStr + opLen + startPos, (size_t) (len - 1));
480 182 : rightSide[len - 1] = '\0';
481 : CondResult ret;
482 :
483 : parseSingleEnd:
484 186 : ret = evalCondition (key, leftSide, cmpOp, rightSide, condition, suffixList, ks, parentKey);
485 186 : if (rightSide) elektraFree (rightSide);
486 186 : elektraFree (leftSide);
487 186 : return ret;
488 : }
489 :
490 17 : static const char * isAssign (Key * key, char * expr, Key * parentKey, KeySet * ks)
491 : {
492 17 : char * firstPtr = expr + 1;
493 17 : char * lastPtr = expr + elektraStrLen (expr) - 3;
494 34 : while (isspace (*firstPtr))
495 0 : ++firstPtr;
496 17 : while (isspace (*lastPtr))
497 0 : --lastPtr;
498 17 : if (*firstPtr != '\'' || *lastPtr != '\'')
499 : {
500 4 : if (lastPtr <= firstPtr)
501 : {
502 0 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (
503 : parentKey, "Invalid syntax: '%s'. Check kdb info conditionals for additional information", expr);
504 0 : return NULL;
505 : }
506 4 : *(lastPtr + 1) = '\0';
507 : Key * lookupKey;
508 4 : if (*firstPtr == '@')
509 : {
510 0 : lookupKey = keyDup (parentKey);
511 0 : ++firstPtr;
512 0 : keyAddName (lookupKey, firstPtr);
513 : }
514 4 : else if (!strncmp (firstPtr, "..", 2) || !strncmp (firstPtr, ".", 1))
515 : {
516 4 : lookupKey = keyDup (key);
517 4 : keyAddName (lookupKey, firstPtr);
518 : }
519 : else
520 : {
521 0 : lookupKey = keyNew (firstPtr, KEY_END);
522 : }
523 4 : Key * assign = ksLookup (ks, lookupKey, KDB_O_NONE);
524 4 : if (!assign)
525 : {
526 0 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (parentKey, "Key %s not found", keyName (lookupKey));
527 0 : keyDel (lookupKey);
528 0 : return NULL;
529 : }
530 : else
531 : {
532 4 : keyDel (lookupKey);
533 4 : return keyString (assign);
534 : }
535 : }
536 : else
537 : {
538 13 : if (firstPtr == lastPtr) // only one quote in the assign string, invalid syntax
539 : {
540 0 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (
541 : parentKey, "Invalid syntax: '%s'. Check kdb info conditionals for additional information", expr);
542 0 : return NULL;
543 : }
544 13 : char * nextMark = strchr (firstPtr + 1, '\'');
545 13 : if (nextMark != lastPtr) // more than two quotes, invalid syntax too
546 : {
547 0 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (
548 : parentKey, "Invalid syntax: '%s'. Check kdb info conditionals for additional information", expr);
549 0 : return NULL;
550 : }
551 13 : *lastPtr = '\0';
552 13 : *firstPtr = '\0';
553 13 : ++firstPtr;
554 13 : return firstPtr;
555 : }
556 : }
557 :
558 154 : static CondResult parseCondition (Key * key, const char * condition, const Key * suffixList, KeySet * ks, Key * parentKey)
559 154 : {
560 154 : CondResult result = FALSE;
561 154 : const char * regexString = "((\\(([^\\(\\)]*)\\)))";
562 : regex_t regex;
563 :
564 154 : if ((regcomp (®ex, regexString, REG_EXTENDED | REG_NEWLINE)))
565 : {
566 0 : ELEKTRA_SET_OUT_OF_MEMORY_ERROR (parentKey,
567 : "Couldn't compile regex: most likely out of memory"); // the regex compiles so the only
568 : // possible error would be out of
569 : // memory
570 0 : ksDel (ks);
571 0 : return ERROR;
572 : }
573 :
574 154 : char * localCondition = elektraStrDup (condition);
575 154 : size_t subMatches = 4;
576 154 : regmatch_t m[subMatches];
577 154 : char * ptr = localCondition;
578 : while (1)
579 186 : {
580 340 : int nomatch = regexec (®ex, ptr, subMatches, m, 0);
581 340 : if (nomatch)
582 : {
583 : break;
584 : }
585 186 : if (m[3].rm_so == -1)
586 : {
587 : result = -1;
588 : break;
589 : }
590 : int startPos;
591 : int endPos;
592 186 : startPos = (int) (m[3].rm_so + (ptr - localCondition));
593 186 : endPos = (int) (m[3].rm_eo + (ptr - localCondition));
594 186 : char * singleCondition = elektraMalloc ((size_t) (endPos - startPos + 1));
595 186 : strncpy (singleCondition, localCondition + startPos, (size_t) (endPos - startPos));
596 186 : singleCondition[endPos - startPos] = '\0';
597 186 : result = parseSingleCondition (key, singleCondition, suffixList, ks, parentKey);
598 4929 : for (int i = startPos - 1; i < endPos + 1; ++i)
599 4743 : localCondition[i] = ' ';
600 186 : localCondition[startPos - 1] = '\'';
601 186 : localCondition[startPos] = (result == TRUE) ? '1' : '0';
602 186 : localCondition[startPos + 1] = '\'';
603 186 : elektraFree (singleCondition);
604 : }
605 154 : elektraFree (localCondition);
606 154 : regfree (®ex);
607 154 : return result;
608 : }
609 :
610 :
611 97 : static CondResult parseConditionString (const Key * meta, const Key * suffixList, Key * parentKey, Key * key, KeySet * ks, Operation op)
612 97 : {
613 97 : const char * conditionString = keyString (meta);
614 97 : const char * regexString1 = "(\\(((.*)?)\\))[[:space:]]*\\?";
615 97 : const char * regexString2 = "\\?[[:space:]]*(\\(((.*)?)\\))";
616 97 : const char * regexString3 = "[[:space:]]*:[[:space:]]*(\\(((.*)?)\\))";
617 : regex_t regex1, regex2, regex3;
618 : CondResult ret;
619 97 : if ((ret = regcomp (®ex1, regexString1, REGEX_FLAGS_CONDITION)))
620 : {
621 0 : ELEKTRA_SET_OUT_OF_MEMORY_ERROR (parentKey,
622 : "Couldn't compile regex: most likely out of memory"); // the regex compiles so the only
623 : // possible error would be out of
624 : // memory
625 0 : ksDel (ks);
626 0 : return ERROR;
627 : }
628 97 : if ((ret = regcomp (®ex2, regexString2, REGEX_FLAGS_CONDITION)))
629 : {
630 0 : ELEKTRA_SET_OUT_OF_MEMORY_ERROR (parentKey,
631 : "Couldn't compile regex: most likely out of memory"); // the regex compiles so the only
632 : // possible error would be out of
633 : // memory
634 0 : regfree (®ex1);
635 0 : ksDel (ks);
636 0 : return ERROR;
637 : }
638 97 : if ((ret = regcomp (®ex3, regexString3, REGEX_FLAGS_CONDITION)))
639 : {
640 0 : ELEKTRA_SET_OUT_OF_MEMORY_ERROR (parentKey,
641 : "Couldn't compile regex: most likely out of memory"); // the regex compiles so the only
642 : // possible error would be out of
643 : // memory
644 0 : regfree (®ex1);
645 0 : regfree (®ex2);
646 0 : ksDel (ks);
647 0 : return ERROR;
648 : }
649 97 : size_t subMatches = 6;
650 97 : regmatch_t m[subMatches];
651 97 : int nomatch = regexec (®ex1, conditionString, subMatches, m, 0);
652 97 : if (nomatch)
653 : {
654 0 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (
655 : parentKey, "Invalid syntax: '%s'. Check kdb info conditionals for additional information", conditionString);
656 0 : regfree (®ex1);
657 0 : regfree (®ex2);
658 0 : regfree (®ex3);
659 0 : ksDel (ks);
660 0 : return ERROR;
661 : }
662 97 : if (m[1].rm_so == -1)
663 : {
664 0 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (
665 : parentKey, "Invalid syntax: '%s'. Check kdb info conditionals for additional information", conditionString);
666 0 : regfree (®ex1);
667 0 : regfree (®ex2);
668 0 : regfree (®ex3);
669 0 : ksDel (ks);
670 0 : return ERROR;
671 : }
672 97 : int startPos = (int) m[1].rm_so;
673 97 : int endPos = (int) m[1].rm_eo;
674 97 : char * condition = elektraMalloc ((size_t) (endPos - startPos + 1));
675 97 : char * thenexpr = NULL;
676 97 : char * elseexpr = NULL;
677 97 : strncpy (condition, conditionString + startPos, (size_t) (endPos - startPos));
678 97 : condition[endPos - startPos] = '\0';
679 97 : nomatch = regexec (®ex2, conditionString, subMatches, m, 0);
680 97 : if (nomatch)
681 : {
682 0 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (
683 : parentKey, "Invalid syntax: '%s'. Check kdb info conditionals for additional information", conditionString);
684 0 : regfree (®ex1);
685 0 : regfree (®ex2);
686 0 : regfree (®ex3);
687 0 : ksDel (ks);
688 0 : return ERROR;
689 : }
690 97 : if (m[1].rm_so == -1)
691 : {
692 0 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (
693 : parentKey, "Invalid syntax: '%s'. Check kdb info conditionals for additional information", conditionString);
694 0 : regfree (®ex1);
695 0 : regfree (®ex2);
696 0 : regfree (®ex3);
697 0 : ksDel (ks);
698 0 : return ERROR;
699 : }
700 :
701 97 : startPos = (int) m[1].rm_so;
702 97 : endPos = (int) m[1].rm_eo;
703 97 : thenexpr = elektraMalloc ((size_t) (endPos - startPos + 1));
704 97 : strncpy (thenexpr, conditionString + startPos, (size_t) (endPos - startPos));
705 97 : thenexpr[endPos - startPos] = '\0';
706 :
707 97 : nomatch = regexec (®ex3, conditionString, subMatches, m, 0);
708 97 : if (!nomatch)
709 : {
710 24 : if (m[1].rm_so == -1)
711 : {
712 0 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (
713 : parentKey, "Invalid syntax: '%s'. Check kdb info conditionals for additional information", conditionString);
714 0 : regfree (®ex1);
715 0 : regfree (®ex2);
716 0 : regfree (®ex3);
717 0 : ksDel (ks);
718 0 : return ERROR;
719 : }
720 24 : thenexpr[strlen (thenexpr) - (size_t) ((m[0].rm_eo - m[0].rm_so))] = '\0';
721 24 : startPos = (int) m[1].rm_so;
722 24 : endPos = (int) m[1].rm_eo;
723 24 : elseexpr = elektraMalloc ((size_t) (endPos - startPos + 1));
724 24 : strncpy (elseexpr, conditionString + startPos, (size_t) (endPos - startPos));
725 24 : elseexpr[endPos - startPos] = '\0';
726 : }
727 :
728 97 : ret = parseCondition (key, condition, suffixList, ks, parentKey);
729 97 : if (ret == TRUE)
730 : {
731 60 : if (op == ASSIGN)
732 : {
733 13 : const char * assign = isAssign (key, thenexpr, parentKey, ks);
734 13 : if (assign != NULL)
735 : {
736 13 : keySetString (key, assign);
737 13 : ret = TRUE;
738 13 : goto CleanUp;
739 : }
740 : else
741 : {
742 : ret = ERROR;
743 : goto CleanUp;
744 : }
745 : }
746 : else
747 : {
748 47 : ret = parseCondition (key, thenexpr, suffixList, ks, parentKey);
749 47 : if (ret == FALSE)
750 : {
751 10 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (parentKey, "Validation of Key %s: %s failed. (%s failed)",
752 : keyName (key) + strlen (keyName (parentKey)) + 1, conditionString,
753 : thenexpr);
754 : }
755 37 : else if (ret == ERROR)
756 : {
757 0 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (
758 : parentKey, "Invalid syntax: '%s'. Check kdb info conditionals for additional information",
759 : thenexpr);
760 : }
761 : }
762 : }
763 37 : else if (ret == FALSE)
764 : {
765 37 : if (elseexpr)
766 : {
767 14 : if (op == ASSIGN)
768 : {
769 4 : const char * assign = isAssign (key, elseexpr, parentKey, ks);
770 4 : if (assign != NULL)
771 : {
772 4 : keySetString (key, assign);
773 4 : ret = TRUE;
774 4 : goto CleanUp;
775 : }
776 : else
777 : {
778 : ret = ERROR;
779 : goto CleanUp;
780 : }
781 : }
782 : else
783 : {
784 10 : ret = parseCondition (key, elseexpr, suffixList, ks, parentKey);
785 :
786 10 : if (ret == FALSE)
787 : {
788 2 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (parentKey, "Validation of Key %s: %s failed. (%s failed)",
789 : keyName (key) + strlen (keyName (parentKey)) + 1,
790 : conditionString, elseexpr);
791 : }
792 8 : else if (ret == ERROR)
793 : {
794 0 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (
795 : parentKey, "Invalid syntax: '%s'. Check kdb info conditionals for additional information",
796 : elseexpr);
797 : }
798 : }
799 : }
800 : else
801 : {
802 : ret = NOEXPR;
803 : }
804 : }
805 0 : else if (ret == ERROR)
806 : {
807 0 : ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (
808 : parentKey, "Invalid syntax: '%s'. Check kdb info conditionals for additional information", condition);
809 : }
810 :
811 : CleanUp:
812 97 : elektraFree (condition);
813 97 : elektraFree (thenexpr);
814 97 : if (elseexpr) elektraFree (elseexpr);
815 97 : regfree (®ex1);
816 97 : regfree (®ex2);
817 97 : regfree (®ex3);
818 97 : ksDel (ks);
819 97 : return ret;
820 : }
821 :
822 97 : static CondResult evaluateKey (const Key * meta, const Key * suffixList, Key * parentKey, Key * key, KeySet * ks, Operation op)
823 : {
824 : CondResult result;
825 97 : result = parseConditionString (meta, suffixList, parentKey, key, ksDup (ks), op);
826 97 : if (result == ERROR)
827 : {
828 : return ERROR;
829 : }
830 97 : else if (result == FALSE && op != ASSIGN)
831 : {
832 : return ERROR;
833 : }
834 85 : else if (result == TRUE && op != ASSIGN)
835 : {
836 : return TRUE;
837 : }
838 40 : else if (result == NOEXPR)
839 : {
840 : return NOEXPR;
841 : }
842 17 : return TRUE;
843 : }
844 :
845 12 : static CondResult evalMultipleConditions (Key * key, const Key * meta, const Key * suffixList, Key * parentKey, KeySet * returned)
846 : {
847 12 : int countSucceeded = 0;
848 12 : int countFailed = 0;
849 12 : int countNoexpr = 0;
850 12 : KeySet * condKS = elektraMetaArrayToKS (key, keyName (meta));
851 : Key * c;
852 12 : CondResult result = FALSE;
853 60 : while ((c = ksNext (condKS)) != NULL)
854 : {
855 36 : if (!keyCmp (c, meta)) continue;
856 24 : result = evaluateKey (c, suffixList, parentKey, key, returned, CONDITION);
857 24 : if (result == TRUE)
858 12 : ++countSucceeded;
859 12 : else if (result == ERROR)
860 0 : ++countFailed;
861 12 : else if (result == NOEXPR)
862 12 : ++countNoexpr;
863 : }
864 12 : ksDel (condKS);
865 12 : if (!strcmp (keyBaseName (meta), "all"))
866 : {
867 : // all conditions must evaluate to TRUE
868 4 : if (countFailed || countNoexpr)
869 : return ERROR;
870 : else
871 0 : return TRUE;
872 : }
873 8 : else if (!strcmp (keyBaseName (meta), "any"))
874 : {
875 : // at least one conditional must evaluate to TRUE
876 4 : if (countSucceeded)
877 : return TRUE;
878 : else
879 0 : return ERROR;
880 : }
881 : else
882 : {
883 : // no condition must evaluate to FALSE
884 4 : if (countFailed)
885 : return ERROR;
886 : else
887 4 : return TRUE;
888 : }
889 : }
890 :
891 125 : int elektraConditionalsGet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED)
892 : {
893 125 : if (!strcmp (keyName (parentKey), "system/elektra/modules/conditionals"))
894 : {
895 42 : KeySet * contract = ksNew (
896 : 30, keyNew ("system/elektra/modules/conditionals", KEY_VALUE, "conditionals plugin waits for your orders", KEY_END),
897 : keyNew ("system/elektra/modules/conditionals/exports", KEY_END),
898 : keyNew ("system/elektra/modules/conditionals/exports/get", KEY_FUNC, elektraConditionalsGet, KEY_END),
899 : keyNew ("system/elektra/modules/conditionals/exports/set", KEY_FUNC, elektraConditionalsSet, KEY_END),
900 : #include ELEKTRA_README
901 : keyNew ("system/elektra/modules/conditionals/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END);
902 42 : ksAppend (returned, contract);
903 42 : ksDel (contract);
904 :
905 42 : return 1; /* success */
906 : }
907 : Key * cur;
908 83 : ksRewind (returned);
909 83 : CondResult ret = FALSE;
910 531 : while ((cur = ksNext (returned)) != NULL)
911 : {
912 365 : Key * conditionMeta = (Key *) keyGetMeta (cur, "check/condition");
913 365 : Key * assignMeta = (Key *) keyGetMeta (cur, "assign/condition");
914 365 : Key * suffixList = (Key *) keyGetMeta (cur, "condition/validsuffix");
915 365 : Key * anyConditionMeta = (Key *) keyGetMeta (cur, "check/condition/any");
916 365 : Key * allConditionMeta = (Key *) keyGetMeta (cur, "check/condition/all");
917 365 : Key * noneConditionMeta = (Key *) keyGetMeta (cur, "check/condition/none");
918 :
919 365 : if (conditionMeta)
920 : {
921 : CondResult result;
922 :
923 48 : result = evaluateKey (conditionMeta, suffixList, parentKey, cur, returned, CONDITION);
924 48 : if (result == NOEXPR)
925 : {
926 5 : ret |= TRUE;
927 : }
928 : else
929 : {
930 43 : ret |= result;
931 : }
932 : }
933 317 : else if (allConditionMeta)
934 : {
935 : CondResult result;
936 0 : result = evalMultipleConditions (cur, allConditionMeta, suffixList, parentKey, returned);
937 0 : ret |= result;
938 : }
939 317 : else if (anyConditionMeta)
940 : {
941 : CondResult result;
942 0 : result = evalMultipleConditions (cur, anyConditionMeta, suffixList, parentKey, returned);
943 0 : ret |= result;
944 : }
945 317 : else if (noneConditionMeta)
946 : {
947 : CondResult result;
948 0 : result = evalMultipleConditions (cur, noneConditionMeta, suffixList, parentKey, returned);
949 0 : ret |= result;
950 : }
951 :
952 365 : if (assignMeta)
953 : {
954 18 : if (keyString (assignMeta)[0] == '#')
955 : {
956 6 : KeySet * assignKS = elektraMetaArrayToKS (cur, "assign/condition");
957 : Key * a;
958 20 : while ((a = ksNext (assignKS)) != NULL)
959 : {
960 14 : if (keyCmp (a, assignMeta) == 0) continue;
961 8 : CondResult result = evaluateKey (a, suffixList, parentKey, cur, returned, ASSIGN);
962 8 : if (result == TRUE)
963 : {
964 6 : ret |= TRUE;
965 6 : break;
966 : }
967 2 : else if (result == NOEXPR)
968 : {
969 2 : ret |= TRUE;
970 : }
971 : else
972 : {
973 : ret |= ERROR;
974 : }
975 : }
976 6 : ksDel (assignKS);
977 : }
978 : else
979 : {
980 12 : ret |= evaluateKey (assignMeta, suffixList, parentKey, cur, returned, ASSIGN);
981 : }
982 : }
983 : }
984 83 : if (ret == TRUE) keySetMeta (parentKey, "error", 0);
985 : return ret; /* success */
986 : }
987 :
988 :
989 23 : int elektraConditionalsSet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED)
990 : {
991 : Key * cur;
992 23 : ksRewind (returned);
993 23 : CondResult ret = FALSE;
994 171 : while ((cur = ksNext (returned)) != NULL)
995 : {
996 125 : Key * conditionMeta = (Key *) keyGetMeta (cur, "check/condition");
997 125 : Key * assignMeta = (Key *) keyGetMeta (cur, "assign/condition");
998 125 : Key * suffixList = (Key *) keyGetMeta (cur, "condition/validsuffix");
999 125 : Key * anyConditionMeta = (Key *) keyGetMeta (cur, "check/condition/any");
1000 125 : Key * allConditionMeta = (Key *) keyGetMeta (cur, "check/condition/all");
1001 125 : Key * noneConditionMeta = (Key *) keyGetMeta (cur, "check/condition/none");
1002 :
1003 125 : if (conditionMeta)
1004 : {
1005 : CondResult result;
1006 :
1007 4 : result = evaluateKey (conditionMeta, suffixList, parentKey, cur, returned, CONDITION);
1008 4 : if (result == NOEXPR)
1009 : {
1010 2 : ret |= TRUE;
1011 : }
1012 : else
1013 : {
1014 2 : ret |= result;
1015 : }
1016 : }
1017 121 : else if (allConditionMeta)
1018 : {
1019 : CondResult result;
1020 4 : result = evalMultipleConditions (cur, allConditionMeta, suffixList, parentKey, returned);
1021 4 : ret |= result;
1022 : }
1023 117 : else if (anyConditionMeta)
1024 : {
1025 : CondResult result;
1026 4 : result = evalMultipleConditions (cur, anyConditionMeta, suffixList, parentKey, returned);
1027 4 : ret |= result;
1028 : }
1029 113 : else if (noneConditionMeta)
1030 : {
1031 : CondResult result;
1032 4 : result = evalMultipleConditions (cur, noneConditionMeta, suffixList, parentKey, returned);
1033 4 : ret |= result;
1034 : }
1035 :
1036 125 : if (assignMeta)
1037 : {
1038 1 : if (keyString (assignMeta)[0] == '#')
1039 : {
1040 0 : KeySet * assignKS = elektraMetaArrayToKS (cur, "assign/condition");
1041 : Key * a;
1042 0 : while ((a = ksNext (assignKS)) != NULL)
1043 : {
1044 0 : if (keyCmp (a, assignMeta) == 0) continue;
1045 0 : CondResult result = evaluateKey (a, suffixList, parentKey, cur, returned, ASSIGN);
1046 0 : if (result == TRUE)
1047 : {
1048 0 : ret |= TRUE;
1049 0 : break;
1050 : }
1051 0 : else if (result == NOEXPR)
1052 : {
1053 0 : ret |= TRUE;
1054 : }
1055 : else
1056 : {
1057 : ret |= ERROR;
1058 : }
1059 : }
1060 0 : ksDel (assignKS);
1061 : }
1062 : else
1063 : {
1064 1 : ret |= evaluateKey (assignMeta, suffixList, parentKey, cur, returned, ASSIGN);
1065 : }
1066 : }
1067 : }
1068 23 : if (ret == TRUE) keySetMeta (parentKey, "error", 0);
1069 23 : return ret;
1070 : }
1071 :
1072 166 : Plugin * ELEKTRA_PLUGIN_EXPORT
1073 : {
1074 : // clang-format off
1075 166 : return elektraPluginExport ("conditionals",
1076 : ELEKTRA_PLUGIN_GET, &elektraConditionalsGet,
1077 : ELEKTRA_PLUGIN_SET, &elektraConditionalsSet,
1078 : ELEKTRA_PLUGIN_END);
1079 : }
|