LCOV - code coverage report
Current view: top level - src/plugins/range - range.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 165 182 90.7 %
Date: 2019-09-12 12:28:41 Functions: 11 12 91.7 %

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

Generated by: LCOV version 1.13