LCOV - code coverage report
Current view: top level - src/plugins/hexnumber - hexnumber.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 115 150 76.7 %
Date: 2019-09-12 12:28:41 Functions: 13 14 92.9 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief Source for hexnumber plugin
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  *
       8             :  */
       9             : 
      10             : #include "hexnumber.h"
      11             : 
      12             : #include <kdbease.h>
      13             : #include <kdberrors.h>
      14             : #include <kdbhelper.h>
      15             : #include <stdbool.h>
      16             : #include <stdio.h>
      17             : #include <stdlib.h>
      18             : 
      19             : typedef struct
      20             : {
      21             :         bool forceConversion;
      22             :         KeySet * integerTypes;
      23             : } HexnumberData;
      24             : 
      25             : /**
      26             :  * Creates a KeySet representing the contract of this plugin.
      27             :  */
      28          37 : static KeySet * elektraContract (void)
      29             : {
      30          37 :         return ksNew (
      31             :                 30,
      32             :                 keyNew ("system/elektra/modules/" ELEKTRA_HEXNUMBER_PLUGIN_NAME, KEY_VALUE, "hexnumber plugin waits for your orders",
      33             :                         KEY_END),
      34             :                 keyNew ("system/elektra/modules/" ELEKTRA_HEXNUMBER_PLUGIN_NAME "/exports", KEY_END),
      35             :                 keyNew ("system/elektra/modules/" ELEKTRA_HEXNUMBER_PLUGIN_NAME "/exports/get", KEY_FUNC, elektraHexnumberGet, KEY_END),
      36             :                 keyNew ("system/elektra/modules/" ELEKTRA_HEXNUMBER_PLUGIN_NAME "/exports/set", KEY_FUNC, elektraHexnumberSet, KEY_END),
      37             :                 keyNew ("system/elektra/modules/" ELEKTRA_HEXNUMBER_PLUGIN_NAME "/exports/close", KEY_FUNC, elektraHexnumberClose, KEY_END),
      38             : 
      39             : #include ELEKTRA_README
      40             : 
      41             :                 keyNew ("system/elektra/modules/" ELEKTRA_HEXNUMBER_PLUGIN_NAME "/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END),
      42             :                 KS_END);
      43             : }
      44             : 
      45             : /**
      46             :  * Converts a Key with a hexadecimal number value to a Key with a decimal value.
      47             :  *
      48             :  * @pre The key has to actually contain a hexadecimal number string.
      49             :  *
      50             :  * @param key The Key which should be converted.
      51             :  * @param parentKey The parent Key used to set errors.
      52             :  *
      53             :  * @retval #ELEKTRA_PLUGIN_STATUS_SUCCESS if the Key was successfully converted
      54             :  * @retval #ELEKTRA_PLUGIN_STATUS_ERROR if the Key could not be converted
      55             :  */
      56          41 : static int convertHexToDec (Key * key, Key * parentKey)
      57             : {
      58             :         // get hex string from key
      59          41 :         const char * hexValue = keyString (key);
      60             : 
      61          41 :         int errnoSaved = errno;
      62             : 
      63             :         // TODO: use supermacro from PR #1850 once merged
      64             :         // convert hex string to long long int
      65          41 :         errno = 0;
      66             :         char * endPtr;
      67          41 :         unsigned long long int value = strtoull (hexValue, &endPtr, 16);
      68          41 :         if (errno == ERANGE && value == ULLONG_MAX)
      69             :         {
      70           0 :                 errno = errnoSaved;
      71           0 :                 ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (parentKey, "Hexadecimal number %s out of range 0 to %llu", hexValue, ULLONG_MAX);
      72           0 :                 return ELEKTRA_PLUGIN_STATUS_ERROR;
      73             :         }
      74          41 :         else if ((errno != 0 && value == 0) || endPtr == hexValue || *endPtr != '\0')
      75             :         {
      76           0 :                 errno = errnoSaved;
      77           0 :                 ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "Hexadecimal number '%s' could not be read", hexValue);
      78           0 :                 return ELEKTRA_PLUGIN_STATUS_ERROR;
      79             :         }
      80          41 :         errno = errnoSaved;
      81             : 
      82             :         // convert long long int back to string (formatted as decimal)
      83          41 :         int result = snprintf (NULL, 0, "%llu", value);
      84          41 :         if (result < 0)
      85             :         {
      86           0 :                 ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "Unable to convert '%s' into decimal", hexValue);
      87           0 :                 return ELEKTRA_PLUGIN_STATUS_ERROR;
      88             :         }
      89             : 
      90          41 :         const size_t length = (size_t) result + 1;
      91          41 :         char * decValue = elektraMalloc (length);
      92          41 :         if (!decValue)
      93             :         {
      94           0 :                 ELEKTRA_MALLOC_ERROR (parentKey, length);
      95           0 :                 return ELEKTRA_PLUGIN_STATUS_ERROR;
      96             :         }
      97             : 
      98          41 :         result = snprintf (decValue, length, "%llu", value);
      99          41 :         if (result < 0)
     100             :         {
     101           0 :                 ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "Unable to convert '%s' into decimal", hexValue);
     102           0 :                 elektraFree (decValue);
     103           0 :                 return ELEKTRA_PLUGIN_STATUS_ERROR;
     104             :         }
     105             : 
     106             :         // set decimal string in key and set internal metadata
     107          41 :         keySetString (key, decValue);
     108          41 :         keySetMeta (key, ELEKTRA_HEXNUMBER_META_KEY, "1");
     109             : 
     110          41 :         elektraFree (decValue);
     111             : 
     112          41 :         return ELEKTRA_PLUGIN_STATUS_SUCCESS;
     113             : }
     114             : 
     115             : /**
     116             :  * Converts a Key with a decimal number value to a Key with a hexadecimal value.
     117             :  *
     118             :  * @pre The key has to actually contain a decimal number string.
     119             :  *
     120             :  * @param key The Key whose value should be converted.
     121             :  * @param parentKey The parent Key used to set errors.
     122             :  *
     123             :  * @retval #ELEKTRA_PLUGIN_STATUS_SUCCESS if the Key was successfully converted
     124             :  * @retval #ELEKTRA_PLUGIN_STATUS_ERROR if the Key could not be converted
     125             :  */
     126           6 : static int convertDecToHex (Key * key, Key * parentKey)
     127             : {
     128             :         // get decimal string from key
     129           6 :         const char * decValue = keyString (key);
     130             : 
     131           6 :         int errnoSaved = errno;
     132             : 
     133             :         // convert hex string to long long int
     134           6 :         errno = 0;
     135             :         char * endPtr;
     136           6 :         unsigned long long int value = strtoull (decValue, &endPtr, 10);
     137           6 :         if (errno == ERANGE && value == ULLONG_MAX)
     138             :         {
     139           0 :                 errno = errnoSaved;
     140           0 :                 ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (parentKey, "Decimal number %s out of range 0 to %llu", decValue, ULLONG_MAX);
     141           0 :                 return ELEKTRA_PLUGIN_STATUS_ERROR;
     142             :         }
     143           6 :         else if ((errno != 0 && value == 0) || endPtr == decValue)
     144             :         {
     145           0 :                 errno = errnoSaved;
     146           0 :                 ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "Decimal number '%s' could not be read", decValue);
     147           0 :                 return ELEKTRA_PLUGIN_STATUS_ERROR;
     148             :         }
     149           6 :         errno = errnoSaved;
     150             : 
     151             :         // convert long long int back to string (formatted as hexadecimal)
     152           6 :         const int result = snprintf (NULL, 0, "0x%llx", value);
     153           6 :         if (result < 0)
     154             :         {
     155           0 :                 ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "Unable to convert '%s' into hexadecimal", decValue);
     156           0 :                 return ELEKTRA_PLUGIN_STATUS_ERROR;
     157             :         }
     158             : 
     159           6 :         const size_t length = (size_t) result + 1;
     160           6 :         char * hexValue = elektraMalloc (length);
     161           6 :         if (!hexValue)
     162             :         {
     163           0 :                 ELEKTRA_MALLOC_ERROR (parentKey, length);
     164           0 :                 return ELEKTRA_PLUGIN_STATUS_ERROR;
     165             :         }
     166             : 
     167           6 :         if (snprintf (hexValue, length, "0x%llx", value) < 0)
     168             :         {
     169           0 :                 ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "Unable to convert '%s' into hexadecimal", decValue);
     170           0 :                 elektraFree (hexValue);
     171           0 :                 return ELEKTRA_PLUGIN_STATUS_ERROR;
     172             :         }
     173             : 
     174             :         // set hex string in key and unset internal metadata
     175           6 :         keySetString (key, hexValue);
     176           6 :         keySetMeta (key, ELEKTRA_HEXNUMBER_META_KEY, "0");
     177             : 
     178             : 
     179           6 :         elektraFree (hexValue);
     180             : 
     181           6 :         return ELEKTRA_PLUGIN_STATUS_SUCCESS;
     182             : }
     183             : 
     184             : /**
     185             :  * Checks whether a given Key's value is a hexadecimal string.
     186             :  *
     187             :  * @param key The Key that should be checked.
     188             :  *
     189             :  * @retval #true if the Key's value starts with 0x
     190             :  * @retval #false otherwise
     191             :  */
     192          66 : static bool isHexString (const Key * key)
     193             : {
     194          66 :         return elektraStrNCaseCmp (keyString (key), "0x", 2) == 0;
     195             : }
     196             : 
     197             : /**
     198             :  * Checks whether a given Key is specified to have a hexadecimal base.
     199             :  *
     200             :  * @param key The Key that should be checked.
     201             :  *
     202             :  * @retval #true if the Key's metadata contains the key /unit/base and its value is hex
     203             :  * @retval #false otherwise
     204             :  */
     205          66 : static bool isHexUnitBase (const Key * key)
     206             : {
     207          66 :         const Key * unitBaseMeta = keyGetMeta (key, "unit/base");
     208          66 :         return elektraStrCmp (keyString (unitBaseMeta), "hex") == 0;
     209             : }
     210             : 
     211             : /**
     212             :  * Filter function used in hasType() for call to elektraKsFilter()
     213             :  */
     214         205 : static int __hasTypeFilter (const Key * key, void * argument)
     215             : {
     216         205 :         const char * type = (const char *) argument;
     217         205 :         return keyIsString (key) && strcmp (type, keyString (key)) == 0;
     218             : }
     219             : 
     220             : /**
     221             :  * Checks whether a given key has one of the given types.
     222             :  *
     223             :  * @param key The Key that should be checked.
     224             :  * @param types An array of strings containing possible values for the /type metadata.
     225             :  * @param typeCount The number of strings in the @p types array.
     226             :  *
     227             :  * @retval #true if the Key's type metadata is contained in the types list
     228             :  * @retval #false otherwise
     229             :  */
     230          62 : static bool hasType (const Key * key, KeySet * types)
     231             : {
     232          62 :         const Key * typeMeta = keyGetMeta (key, "type");
     233          62 :         if (!typeMeta || !types)
     234             :         {
     235             :                 return false;
     236             :         }
     237             : 
     238          58 :         const char * type = keyString (typeMeta);
     239          58 :         KeySet * res = ksNew ((size_t) ksGetSize (types), KS_END);
     240          58 :         elektraKsFilter (res, types, &__hasTypeFilter, (void *) type);
     241             : 
     242          58 :         const ssize_t size = ksGetSize (res);
     243          58 :         ksDel (res);
     244             : 
     245          58 :         return size > 0;
     246             : }
     247             : 
     248             : /**
     249             :  * Checks whether a given Key's type metadata is #ELEKTRA_HEXNUMBER_META_TYPE.
     250             :  *
     251             :  * @param key The Key that should be checked.
     252             :  *
     253             :  * @retval #true if the Key's type metadata is #ELEKTRA_HEXNUMBER_META_TYPE
     254             :  * @retval #false otherwise
     255             :  */
     256          20 : static bool hasHexType (const Key * key)
     257             : {
     258          20 :         const Key * typeMeta = keyGetMeta (key, ELEKTRA_HEXNUMBER_META_KEY);
     259          20 :         return typeMeta && elektraStrCmp (keyString (typeMeta), "1") == 0;
     260             : }
     261             : 
     262             : /**
     263             :  * Parse the configuration as described in README.md
     264             :  * @param config the configuration KeySet provided by elektraPluginGetConfig
     265             :  * @param data pointer to the struct to store the configuration in
     266             :  * @param errorKey Key used to store error information
     267             :  */
     268          23 : int parseConfig (KeySet * config, HexnumberData * data, Key * errorKey)
     269             : {
     270          23 :         Key * forceKey = ksLookupByName (config, "/force", 0);
     271          23 :         if (forceKey)
     272             :         {
     273           2 :                 data->forceConversion = true;
     274             :         }
     275             : 
     276          23 :         Key * typesKey = keyNew ("/accept/type", KEY_END);
     277          23 :         KeySet * types = elektraArrayGet (typesKey, config);
     278          23 :         keyDel (typesKey);
     279             : 
     280          23 :         if (!types)
     281             :         {
     282           0 :                 ELEKTRA_SET_VALIDATION_SYNTACTIC_ERROR (errorKey, "Could not parse config. Types not set correctly");
     283           0 :                 return ELEKTRA_PLUGIN_STATUS_ERROR;
     284             :         }
     285             : 
     286          23 :         data->integerTypes = types;
     287             : 
     288          23 :         return ELEKTRA_PLUGIN_STATUS_SUCCESS;
     289             : }
     290             : 
     291             : /**
     292             :  * Establish the plugin contract and convert all hexadecimal values in the KeySet to decimal.
     293             :  *
     294             :  * @param handle This parameter stores the configuration of the plugin.
     295             :  * @param returned This parameter specifies the key set that this function updates.
     296             :  * @param parentKey The function stores information about errors/warnings in this parameter.
     297             :  *
     298             :  * @retval #ELEKTRA_PLUGIN_STATUS_SUCCESS if any keys were updated
     299             :  * @retval #ELEKTRA_PLUGIN_STATUS_NO_UPDATE if \p returned was not modified
     300             :  * @retval #ELEKTRA_PLUGIN_STATUS_ERROR on failure
     301             :  */
     302          59 : int elektraHexnumberGet (Plugin * handle, KeySet * returned, Key * parentKey)
     303             : {
     304          59 :         if (!elektraStrCmp (keyName (parentKey), "system/elektra/modules/" ELEKTRA_HEXNUMBER_PLUGIN_NAME))
     305             :         {
     306          37 :                 KeySet * contract = elektraContract ();
     307          37 :                 ksAppend (returned, contract);
     308          37 :                 ksDel (contract);
     309             : 
     310          37 :                 return ELEKTRA_PLUGIN_STATUS_SUCCESS;
     311             :         }
     312             : 
     313          22 :         HexnumberData * data = elektraPluginGetData (handle);
     314          22 :         if (!data)
     315             :         {
     316          22 :                 KeySet * config = elektraPluginGetConfig (handle);
     317          22 :                 data = elektraCalloc (sizeof (HexnumberData));
     318          22 :                 if (parseConfig (config, data, parentKey) == ELEKTRA_PLUGIN_STATUS_ERROR)
     319             :                 {
     320           0 :                         elektraFree (data);
     321           0 :                         return ELEKTRA_PLUGIN_STATUS_ERROR;
     322             :                 }
     323          22 :                 elektraPluginSetData (handle, data);
     324             :         }
     325             : 
     326             :         Key * cur;
     327          22 :         ksRewind (returned);
     328             : 
     329          22 :         KeySet * defaultIntegerTypes = ksNew (7, keyNew ("system/accept/type/#0", KEY_VALUE, "byte", KEY_END),
     330             :                                               keyNew ("system/accept/type/#1", KEY_VALUE, "short", KEY_END),
     331             :                                               keyNew ("system/accept/type/#2", KEY_VALUE, "long", KEY_END),
     332             :                                               keyNew ("system/accept/type/#3", KEY_VALUE, "long_long", KEY_END),
     333             :                                               keyNew ("system/accept/type/#4", KEY_VALUE, "unsigned_short", KEY_END),
     334             :                                               keyNew ("system/accept/type/#5", KEY_VALUE, "unsigned_long", KEY_END),
     335             :                                               keyNew ("system/accept/type/#6", KEY_VALUE, "unsigned_long_long", KEY_END), KS_END);
     336             : 
     337          22 :         int status = ELEKTRA_PLUGIN_STATUS_NO_UPDATE;
     338         110 :         while ((cur = ksNext (returned)) != NULL)
     339             :         {
     340          66 :                 if (!keyIsString (cur))
     341             :                 {
     342           0 :                         continue;
     343             :                 }
     344             : 
     345          66 :                 bool hexString = isHexString (cur);
     346          66 :                 if (isHexUnitBase (cur))
     347             :                 {
     348           6 :                         if (hexString)
     349             :                         {
     350           6 :                                 status |= convertHexToDec (cur, parentKey);
     351             :                         }
     352             :                         else
     353             :                         {
     354           0 :                                 ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (
     355             :                                         parentKey, "Key '%s' has unit/base metadata set as hex but value '%s' does not start with 0x",
     356             :                                         keyName (cur), keyString (cur));
     357           0 :                                 status |= ELEKTRA_PLUGIN_STATUS_ERROR;
     358             :                         }
     359             :                 }
     360          60 :                 else if (hexString && (data->forceConversion || hasType (cur, data->integerTypes) || hasType (cur, defaultIntegerTypes)))
     361             :                 {
     362          35 :                         status |= convertHexToDec (cur, parentKey);
     363             :                 }
     364             :         }
     365             : 
     366          22 :         ksDel (defaultIntegerTypes);
     367             : 
     368          22 :         return status;
     369             : }
     370             : 
     371             : /**
     372             :  * Convert all values in the KeySet originally stored as hexadecimal (marked by type metdata) to hexadecimal.
     373             :  *
     374             :  * @param handle This parameter stores the configuration of the plugin.
     375             :  * @param returned This parameter specifies the key set that this function updates.
     376             :  * @param parentKey The function stores information about errors/warnings in this parameter.
     377             :  *
     378             :  * @retval #ELEKTRA_PLUGIN_STATUS_SUCCESS if any keys were updated
     379             :  * @retval #ELEKTRA_PLUGIN_STATUS_NO_UPDATE if \p returned was not modified
     380             :  * @retval #ELEKTRA_PLUGIN_STATUS_ERROR on failure
     381             :  */
     382          10 : int elektraHexnumberSet (Plugin * handle, KeySet * returned, Key * parentKey)
     383             : {
     384          10 :         HexnumberData * data = elektraPluginGetData (handle);
     385          10 :         if (!data)
     386             :         {
     387           1 :                 KeySet * config = elektraPluginGetConfig (handle);
     388           1 :                 data = elektraCalloc (sizeof (HexnumberData));
     389           1 :                 if (parseConfig (config, data, parentKey) == ELEKTRA_PLUGIN_STATUS_ERROR)
     390             :                 {
     391           0 :                         elektraFree (data);
     392           0 :                         return ELEKTRA_PLUGIN_STATUS_ERROR;
     393             :                 }
     394           1 :                 elektraPluginSetData (handle, data);
     395             :         }
     396             : 
     397             :         Key * cur;
     398          10 :         ksRewind (returned);
     399             : 
     400          10 :         int status = ELEKTRA_PLUGIN_STATUS_NO_UPDATE;
     401          40 :         while ((cur = ksNext (returned)) != NULL)
     402             :         {
     403          20 :                 if (keyIsString (cur) && hasHexType (cur))
     404             :                 {
     405           6 :                         status |= convertDecToHex (cur, parentKey);
     406             :                 }
     407             :         }
     408             : 
     409             :         return status;
     410             : }
     411             : 
     412         103 : int elektraHexnumberClose (Plugin * handle, Key * parentKey ELEKTRA_UNUSED)
     413             : {
     414         103 :         HexnumberData * data = elektraPluginGetData (handle);
     415         103 :         if (data)
     416             :         {
     417          23 :                 ksDel (data->integerTypes);
     418          23 :                 elektraFree (data);
     419          23 :                 elektraPluginSetData (handle, NULL);
     420             :         }
     421             : 
     422         103 :         return ELEKTRA_PLUGIN_STATUS_SUCCESS;
     423             : }
     424             : 
     425             : /**
     426             :  * Exports the plugin to be used by Elektra.
     427             :  */
     428         103 : Plugin * ELEKTRA_PLUGIN_EXPORT
     429             : {
     430             :         // clang-format off
     431         103 :         return elektraPluginExport (ELEKTRA_HEXNUMBER_PLUGIN_NAME,
     432             :                                     ELEKTRA_PLUGIN_GET, &elektraHexnumberGet,
     433             :                                     ELEKTRA_PLUGIN_SET, &elektraHexnumberSet,
     434             :                                     ELEKTRA_PLUGIN_CLOSE, &elektraHexnumberClose,
     435             :                                     ELEKTRA_PLUGIN_END);
     436             : }

Generated by: LCOV version 1.13