LCOV - code coverage report
Current view: top level - src/plugins/type - types.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 197 236 83.5 %
Date: 2019-09-12 12:28:41 Functions: 16 23 69.6 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  *
       8             :  */
       9             : 
      10             : #include "types.h"
      11             : #include "type.h"
      12             : 
      13             : #include <ctype.h>
      14             : #include <stdio.h>
      15             : #include <stdlib.h>
      16             : #include <string.h>
      17             : 
      18             : #include <kdbhelper.h>
      19             : 
      20             : #include <kdbease.h>
      21             : #include <kdberrors.h>
      22             : 
      23             : #define CHECK_TYPE(key, var, toValue)                                                                                                      \
      24             :         {                                                                                                                                  \
      25             :                 if (strlen (keyString (key)) == 0 || toValue (key, &var) != 1)                                                             \
      26             :                 {                                                                                                                          \
      27             :                         return false;                                                                                                      \
      28             :                 }                                                                                                                          \
      29             :         }
      30             : 
      31             : #define CHECK_TYPE_REVERSIBLE(key, var, toString)                                                                                          \
      32             :         {                                                                                                                                  \
      33             :                 char * string = toString (var);                                                                                            \
      34             :                 if (strcmp (keyString (key), string) != 0)                                                                                 \
      35             :                 {                                                                                                                          \
      36             :                         elektraFree (string);                                                                                              \
      37             :                         return false;                                                                                                      \
      38             :                 }                                                                                                                          \
      39             :                 elektraFree (string);                                                                                                      \
      40             :         }
      41             : 
      42           0 : bool elektraTypeCheckAny (const Key * key ELEKTRA_UNUSED)
      43             : {
      44           0 :         return true;
      45             : }
      46             : 
      47        1045 : bool elektraTypeCheckChar (const Key * key)
      48             : {
      49        1045 :         return strlen (keyString (key)) == 1;
      50             : }
      51             : 
      52             : 
      53      131072 : bool elektraTypeCheckWChar (const Key * key)
      54             : {
      55             :         wchar_t out[2];
      56      131072 :         return mbstowcs (out, keyString (key), 2) == 1;
      57             : }
      58             : 
      59          10 : bool elektraTypeCheckString (const Key * key)
      60             : {
      61          10 :         return strlen (keyString (key)) != 0;
      62             : }
      63             : 
      64          10 : bool elektraTypeCheckWString (const Key * key)
      65             : {
      66          10 :         const char * value = keyString (key);
      67          10 :         size_t max = strlen (value) + 1;
      68          10 :         wchar_t * wvalue = elektraCalloc (sizeof (wchar_t) * max);
      69          10 :         size_t result = mbstowcs (wvalue, value, max);
      70          10 :         elektraFree (wvalue);
      71          10 :         return result > 0 && result < max;
      72             : }
      73             : 
      74         260 : bool elektraTypeNormalizeBoolean (Plugin * handle, Key * key)
      75             : {
      76         260 :         const char * value = keyString (key);
      77             : 
      78         260 :         TypeData * data = elektraPluginGetData (handle);
      79             : 
      80         260 :         const Key * trueOverride = keyGetMeta (key, "check/boolean/true");
      81         260 :         const Key * falseOverride = keyGetMeta (key, "check/boolean/false");
      82             : 
      83         260 :         if ((trueOverride == NULL) != (falseOverride == NULL))
      84             :         {
      85             :                 return false;
      86             :         }
      87         260 :         else if (trueOverride != NULL)
      88             :         {
      89          12 :                 if (strcasecmp (keyString (trueOverride), value) == 0 || strcmp ("1", value) == 0)
      90             :                 {
      91          12 :                         keySetString (key, "1");
      92          12 :                         keySetMeta (key, "origvalue", keyString (trueOverride));
      93          12 :                         return true;
      94             :                 }
      95           0 :                 else if (strcasecmp (keyString (falseOverride), value) == 0 || strcmp ("0", value) == 0)
      96             :                 {
      97           0 :                         keySetString (key, "0");
      98           0 :                         keySetMeta (key, "origvalue", keyString (falseOverride));
      99           0 :                         return true;
     100             :                 }
     101             :                 return false;
     102             :         }
     103             : 
     104             :         const char * origTrueValue;
     105             :         const char * origFalseValue;
     106             : 
     107         248 :         bool restore = data->booleanRestore >= 0;
     108             : 
     109         248 :         if (value[0] == '1' && value[1] == '\0')
     110             :         {
     111          46 :                 if (restore)
     112             :                 {
     113          24 :                         keySetMeta (key, "origvalue", data->booleans[data->booleanRestore].trueValue);
     114             :                 }
     115             : 
     116             :                 return true;
     117             :         }
     118             : 
     119         202 :         if (value[0] == '0' && value[1] == '\0')
     120             :         {
     121          42 :                 if (restore)
     122             :                 {
     123          24 :                         keySetMeta (key, "origvalue", data->booleans[data->booleanRestore].falseValue);
     124             :                 }
     125             : 
     126             :                 return true;
     127             :         }
     128             : 
     129         160 :         char * origValue = elektraStrDup (value);
     130             : 
     131         160 :         origTrueValue = restore ? data->booleans[data->booleanRestore].trueValue : origValue;
     132         160 :         origFalseValue = restore ? data->booleans[data->booleanRestore].falseValue : origValue;
     133             : 
     134         386 :         for (kdb_long_long_t i = 0; i < data->booleanCount; ++i)
     135             :         {
     136         377 :                 if (strcasecmp (data->booleans[i].trueValue, value) == 0)
     137             :                 {
     138          73 :                         keySetString (key, "1");
     139          73 :                         if (data->booleanRestore != -2)
     140             :                         {
     141          69 :                                 keySetMeta (key, "origvalue", origTrueValue);
     142             :                         }
     143          73 :                         elektraFree (origValue);
     144          73 :                         return true;
     145             :                 }
     146         304 :                 else if (strcasecmp (data->booleans[i].falseValue, value) == 0)
     147             :                 {
     148          78 :                         keySetString (key, "0");
     149          78 :                         if (data->booleanRestore != -2)
     150             :                         {
     151          74 :                                 keySetMeta (key, "origvalue", origFalseValue);
     152             :                         }
     153          78 :                         elektraFree (origValue);
     154          78 :                         return true;
     155             :                 }
     156             :         }
     157             : 
     158           9 :         elektraFree (origValue);
     159           9 :         return false;
     160             : }
     161             : 
     162         418 : bool elektraTypeCheckBoolean (const Key * key)
     163             : {
     164         418 :         const char * value = keyString (key);
     165         418 :         return (value[0] == '1' || value[0] == '0') && value[1] == '\0';
     166             : }
     167             : 
     168         187 : bool elektraTypeRestoreBoolean (Plugin * handle ELEKTRA_UNUSED, Key * key)
     169             : {
     170         187 :         const Key * orig = keyGetMeta (key, "origvalue");
     171         187 :         if (orig != NULL)
     172             :         {
     173         164 :                 keySetString (key, keyString (orig));
     174             :         }
     175             : 
     176         187 :         return true;
     177             : }
     178             : 
     179          24 : bool elektraTypeCheckFloat (const Key * key)
     180             : {
     181             :         kdb_float_t value;
     182          24 :         CHECK_TYPE (key, value, elektraKeyToFloat)
     183             :         return true;
     184             : }
     185             : 
     186           0 : bool elektraTypeCheckDouble (const Key * key)
     187             : {
     188             :         kdb_double_t value;
     189           0 :         CHECK_TYPE (key, value, elektraKeyToDouble)
     190             :         return true;
     191             : }
     192             : 
     193             : #ifdef ELEKTRA_HAVE_KDB_LONG_DOUBLE
     194           0 : bool elektraTypeCheckLongDouble (const Key * key)
     195             : {
     196             :         kdb_long_double_t value;
     197           0 :         CHECK_TYPE (key, value, elektraKeyToLongDouble)
     198             :         return true;
     199             : }
     200             : 
     201             : #endif
     202             : 
     203          27 : bool elektraTypeCheckShort (const Key * key)
     204             : {
     205             :         kdb_short_t value;
     206          27 :         CHECK_TYPE (key, value, elektraKeyToShort)
     207          12 :         CHECK_TYPE_REVERSIBLE (key, value, elektraShortToString);
     208          12 :         return true;
     209             : }
     210             : 
     211           0 : bool elektraTypeCheckLong (const Key * key)
     212             : {
     213             :         kdb_long_t value;
     214           0 :         CHECK_TYPE (key, value, elektraKeyToLong)
     215           0 :         CHECK_TYPE_REVERSIBLE (key, value, elektraLongToString);
     216           0 :         return true;
     217             : }
     218             : 
     219           0 : bool elektraTypeCheckLongLong (const Key * key)
     220             : {
     221             :         kdb_long_long_t value;
     222           0 :         CHECK_TYPE (key, value, elektraKeyToLongLong)
     223           0 :         CHECK_TYPE_REVERSIBLE (key, value, elektraLongLongToString);
     224           0 :         return true;
     225             : }
     226             : 
     227          18 : bool elektraTypeCheckUnsignedShort (const Key * key)
     228             : {
     229             :         kdb_unsigned_short_t value;
     230          18 :         CHECK_TYPE (key, value, elektraKeyToUnsignedShort)
     231           6 :         CHECK_TYPE_REVERSIBLE (key, value, elektraUnsignedShortToString);
     232           6 :         return true;
     233             : }
     234             : 
     235           0 : bool elektraTypeCheckUnsignedLong (const Key * key)
     236             : {
     237             :         kdb_unsigned_long_t value;
     238           0 :         CHECK_TYPE (key, value, elektraKeyToUnsignedLong)
     239           0 :         CHECK_TYPE_REVERSIBLE (key, value, elektraUnsignedLongToString);
     240           0 :         return true;
     241             : }
     242             : 
     243           0 : bool elektraTypeCheckUnsignedLongLong (const Key * key)
     244             : {
     245             :         kdb_unsigned_long_long_t value;
     246           0 :         CHECK_TYPE (key, value, elektraKeyToUnsignedLongLong)
     247           0 :         CHECK_TYPE_REVERSIBLE (key, value, elektraUnsignedLongLongToString);
     248           0 :         return true;
     249             : }
     250             : 
     251         111 : static bool enumValidValues (const Key * key, KeySet * validValues, char * delim)
     252             : {
     253             : 
     254         111 :         const Key * maxKey = keyGetMeta (key, "check/enum");
     255         111 :         const char * max = maxKey == NULL ? NULL : keyString (maxKey);
     256             : 
     257         111 :         if (max == NULL)
     258             :         {
     259             :                 return false;
     260             :         }
     261             : 
     262             :         char elem[sizeof ("check/enum/") + ELEKTRA_MAX_ARRAY_SIZE];
     263         111 :         strcpy (elem, "check/enum/");
     264         111 :         char * indexStart = elem + sizeof ("check/enum/") - 1;
     265             : 
     266         111 :         kdb_long_long_t index = 0;
     267         111 :         elektraWriteArrayNumber (indexStart, index);
     268         535 :         while (strcmp (indexStart, max) <= 0)
     269             :         {
     270         313 :                 const Key * enumKey = keyGetMeta (key, elem);
     271         313 :                 const char * name = enumKey != NULL ? keyString (enumKey) : "";
     272         313 :                 if (strlen (name) > 0)
     273             :                 {
     274         303 :                         kdb_unsigned_long_long_t val = index;
     275         303 :                         ksAppendKey (validValues, keyNew (name, KEY_META_NAME, KEY_BINARY, KEY_SIZE, sizeof (kdb_unsigned_long_long_t),
     276             :                                                           KEY_VALUE, &val, KEY_END));
     277             :                 }
     278             : 
     279         313 :                 ++index;
     280         313 :                 elektraWriteArrayNumber (indexStart, index);
     281             :         }
     282             : 
     283         111 :         const Key * multiEnum = keyGetMeta (key, "check/enum/delimiter");
     284         111 :         if (multiEnum != NULL)
     285             :         {
     286          56 :                 const char * delimString = keyString (multiEnum);
     287             : 
     288          56 :                 if (strlen (delimString) != 1)
     289             :                 {
     290           0 :                         ksDel (validValues);
     291           0 :                         return false;
     292             :                 }
     293          56 :                 *delim = delimString[0];
     294             :         }
     295             : 
     296             :         return true;
     297             : }
     298             : 
     299          10 : static char * calculateStringValue (KeySet * validValues, char delimiter, kdb_unsigned_long_long_t value)
     300             : {
     301          10 :         char * stringValue = elektraStrDup ("");
     302             : 
     303          10 :         ksRewind (validValues);
     304          10 :         Key * cur = NULL;
     305          38 :         while ((cur = ksNext (validValues)) != NULL)
     306             :         {
     307          24 :                 const kdb_unsigned_long_long_t * val = keyValue (cur);
     308          24 :                 if (delimiter == 0 && *val == value)
     309             :                 {
     310           4 :                         elektraFree (stringValue);
     311           4 :                         return elektraStrDup (keyName (cur));
     312             :                 }
     313          20 :                 else if (delimiter != 0)
     314             :                 {
     315          18 :                         if (*val == 0 && value == 0 && stringValue[0] == '\0')
     316             :                         {
     317           2 :                                 elektraFree (stringValue);
     318           2 :                                 return elektraStrDup (keyName (cur));
     319             :                         }
     320          16 :                         else if (*val != 0 && (*val & value) == *val)
     321             :                         {
     322           8 :                                 char * tmp = stringValue[0] == '\0' ? elektraFormat ("%s", keyName (cur)) :
     323           2 :                                                                       elektraFormat ("%s%c%s", stringValue, delimiter, keyName (cur));
     324           6 :                                 elektraFree (stringValue);
     325           6 :                                 stringValue = tmp;
     326             : 
     327           6 :                                 value &= ~*val;
     328             :                         }
     329             :                 }
     330             :         }
     331             : 
     332             :         return stringValue;
     333             : }
     334             : 
     335         111 : bool elektraTypeNormalizeEnum (Plugin * handle ELEKTRA_UNUSED, Key * key)
     336             : {
     337         111 :         const Key * normalize = keyGetMeta (key, "check/enum/normalize");
     338         111 :         if (normalize == NULL || strcmp (keyString (normalize), "1") != 0)
     339             :         {
     340             :                 return true;
     341             :         }
     342             : 
     343          52 :         KeySet * validValues = ksNew (0, KS_END);
     344          52 :         char delim = 0;
     345          52 :         if (!enumValidValues (key, validValues, &delim))
     346             :         {
     347             :                 return false;
     348             :         }
     349             : 
     350          52 :         char * values = elektraStrDup (keyString (key));
     351          52 :         char * value = values;
     352             :         char * next;
     353             : 
     354          52 :         if (isdigit (values[0]))
     355             :         {
     356          10 :                 kdb_unsigned_long_long_t val = ELEKTRA_UNSIGNED_LONG_LONG_S (values, NULL, 10);
     357          10 :                 char * origValue = calculateStringValue (validValues, delim, val);
     358          10 :                 if (origValue == NULL)
     359             :                 {
     360           0 :                         ksDel (validValues);
     361           0 :                         elektraFree (values);
     362           0 :                         return false;
     363             :                 }
     364             : 
     365          10 :                 keySetMeta (key, "origvalue", origValue);
     366             : 
     367          10 :                 elektraFree (origValue);
     368          10 :                 ksDel (validValues);
     369          10 :                 elektraFree (values);
     370          10 :                 return true;
     371             :         }
     372             : 
     373          42 :         kdb_unsigned_long_long_t normalized = 0;
     374          42 :         if (delim != 0)
     375             :         {
     376          48 :                 while ((next = strchr (value, delim)) != NULL)
     377             :                 {
     378          20 :                         *next = '\0';
     379          20 :                         Key * cur = ksLookupByName (validValues, value, 0);
     380          20 :                         if (cur == NULL)
     381             :                         {
     382           0 :                                 ksDel (validValues);
     383           0 :                                 elektraFree (values);
     384           0 :                                 return false;
     385             :                         }
     386             : 
     387          20 :                         const kdb_unsigned_long_long_t * val = keyValue (cur);
     388          20 :                         normalized |= *val;
     389          20 :                         value = next + 1;
     390             :                 }
     391             :         }
     392             : 
     393          42 :         Key * cur = ksLookupByName (validValues, value, 0);
     394          42 :         if (cur == NULL)
     395             :         {
     396           0 :                 ksDel (validValues);
     397           0 :                 elektraFree (values);
     398           0 :                 return false;
     399             :         }
     400             : 
     401          42 :         const kdb_unsigned_long_long_t * val = keyValue (cur);
     402          42 :         normalized |= *val;
     403             : 
     404          42 :         ksDel (validValues);
     405          42 :         elektraFree (values);
     406             : 
     407          42 :         char * origValue = elektraStrDup (keyString (key));
     408          42 :         char * normValue = elektraFormat (ELEKTRA_UNSIGNED_LONG_LONG_F, normalized);
     409             : 
     410          42 :         keySetString (key, normValue);
     411          42 :         keySetMeta (key, "origvalue", origValue);
     412             : 
     413          42 :         elektraFree (origValue);
     414          42 :         elektraFree (normValue);
     415             : 
     416          42 :         return true;
     417             : }
     418             : 
     419         139 : bool elektraTypeCheckEnum (const Key * key)
     420             : {
     421         139 :         const Key * normalize = keyGetMeta (key, "check/enum/normalize");
     422         139 :         if (normalize != NULL && strcmp (keyString (normalize), "1") == 0)
     423             :         {
     424             :                 // was already implicitly checked during normalization
     425             :                 return true;
     426             :         }
     427             : 
     428          59 :         const Key * maxKey = keyGetMeta (key, "check/enum");
     429          59 :         const char * max = maxKey == NULL ? NULL : keyString (maxKey);
     430             : 
     431          59 :         if (max == NULL)
     432             :         {
     433             :                 return false;
     434             :         }
     435             : 
     436          59 :         KeySet * validValues = ksNew (0, KS_END);
     437          59 :         char delim = 0;
     438          59 :         if (!enumValidValues (key, validValues, &delim))
     439             :         {
     440             :                 return false;
     441             :         }
     442             : 
     443          59 :         char * values = elektraStrDup (keyString (key));
     444          59 :         char * value = values;
     445             :         char * next;
     446             : 
     447          59 :         if (delim != 0)
     448             :         {
     449          35 :                 while ((next = strchr (value, delim)) != NULL)
     450             :                 {
     451          14 :                         *next = '\0';
     452          14 :                         if (ksLookupByName (validValues, value, 0) == NULL)
     453             :                         {
     454           1 :                                 ksDel (validValues);
     455           1 :                                 elektraFree (values);
     456           1 :                                 return false;
     457             :                         }
     458          13 :                         value = next + 1;
     459             :                 }
     460             :         }
     461             : 
     462          58 :         if (ksLookupByName (validValues, value, 0) == NULL)
     463             :         {
     464           9 :                 ksDel (validValues);
     465           9 :                 elektraFree (values);
     466           9 :                 return false;
     467             :         }
     468             : 
     469             : 
     470          49 :         ksDel (validValues);
     471          49 :         elektraFree (values);
     472             : 
     473          49 :         return true;
     474             : }
     475             : 
     476          63 : bool elektraTypeRestoreEnum (Plugin * handle ELEKTRA_UNUSED, Key * key)
     477             : {
     478          63 :         const Key * orig = keyGetMeta (key, "origvalue");
     479          63 :         if (orig != NULL)
     480             :         {
     481          40 :                 keySetString (key, keyString (orig));
     482             :         }
     483             : 
     484          63 :         return true;
     485             : }
     486             : 
     487          10 : void elektraTypeSetErrorEnum (Plugin * handle ELEKTRA_UNUSED, Key * errorKey, const Key * key)
     488             : {
     489          10 :         const Key * maxKey = keyGetMeta (key, "check/enum");
     490          10 :         const char * max = maxKey == NULL ? NULL : keyString (maxKey);
     491             : 
     492          10 :         if (max == NULL)
     493             :         {
     494           0 :                 ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (
     495             :                         errorKey,
     496             :                         "The type 'enum' failed to match for '%s' with string: '%s'\n"
     497             :                         "No values are allowed (check/enum is an empty array, or parent isn't set to last element)",
     498             :                         keyName (key), keyString (key));
     499           0 :                 return;
     500             :         }
     501             : 
     502          10 :         char * errorMessage = elektraFormat (
     503             :                 "The type 'enum' failed to match for '%s' with string: '%s'\n"
     504             :                 "Allowed values:",
     505             :                 keyName (key), keyString (key));
     506             : 
     507             :         char elem[sizeof ("check/enum/") + ELEKTRA_MAX_ARRAY_SIZE];
     508          10 :         strcpy (elem, "check/enum/");
     509          10 :         char * indexStart = elem + sizeof ("check/enum/") - 1;
     510             : 
     511          10 :         kdb_long_long_t index = 0;
     512          10 :         elektraWriteArrayNumber (indexStart, index);
     513          43 :         while (strcmp (indexStart, max) <= 0)
     514             :         {
     515          23 :                 const Key * enumKey = keyGetMeta (key, elem);
     516          23 :                 const char * name = enumKey != NULL ? keyString (enumKey) : "";
     517          23 :                 if (strlen (name) > 0)
     518             :                 {
     519          23 :                         char * newErrorMessage = elektraFormat ("%s '%s'", errorMessage, name);
     520          23 :                         elektraFree (errorMessage);
     521          23 :                         errorMessage = newErrorMessage;
     522             :                 }
     523             : 
     524          23 :                 ++index;
     525          23 :                 elektraWriteArrayNumber (indexStart, index);
     526             :         }
     527             : 
     528          10 :         ELEKTRA_SET_VALIDATION_SEMANTIC_ERROR (errorKey, errorMessage);
     529          10 :         elektraFree (errorMessage);
     530             : }

Generated by: LCOV version 1.13