LCOV - code coverage report
Current view: top level - src/plugins/conditionals - conditionals.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 374 494 75.7 %
Date: 2019-09-12 12:28:41 Functions: 14 15 93.3 %

          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 (&regex, 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 (&regex, 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 (&regex);
     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 (&regex1, 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 (&regex2, 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 (&regex1);
     635           0 :                 ksDel (ks);
     636           0 :                 return ERROR;
     637             :         }
     638          97 :         if ((ret = regcomp (&regex3, 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 (&regex1);
     645           0 :                 regfree (&regex2);
     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 (&regex1, 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 (&regex1);
     657           0 :                 regfree (&regex2);
     658           0 :                 regfree (&regex3);
     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 (&regex1);
     667           0 :                 regfree (&regex2);
     668           0 :                 regfree (&regex3);
     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 (&regex2, 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 (&regex1);
     685           0 :                 regfree (&regex2);
     686           0 :                 regfree (&regex3);
     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 (&regex1);
     695           0 :                 regfree (&regex2);
     696           0 :                 regfree (&regex3);
     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 (&regex3, 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 (&regex1);
     715           0 :                         regfree (&regex2);
     716           0 :                         regfree (&regex3);
     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 (&regex1);
     816          97 :         regfree (&regex2);
     817          97 :         regfree (&regex3);
     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             : }

Generated by: LCOV version 1.13