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

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief A plugin that converts keys to metakeys and vice versa
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  *
       8             :  */
       9             : 
      10             : #include "keytometa.h"
      11             : 
      12             : #ifndef HAVE_KDBCONFIG
      13             : #include "kdbconfig.h"
      14             : #endif
      15             : 
      16             : 
      17             : #include <errno.h>
      18             : #include <stdbool.h>
      19             : #include <stdlib.h>
      20             : 
      21             : static const char * CONVERT_METANAME = "convert/metaname";
      22             : static const char * CONVERT_TARGET = "convert/to";
      23             : static const char * CONVERT_APPEND_SAMELEVEL = "convert/append/samelevel";
      24             : static const char * CONVERT_APPENDMODE = "convert/append";
      25             : 
      26             : /*
      27             :  * Wrapper for the function comparing by order metadata. As
      28             :  * qsort is not stable returning 0 on missing order may
      29             :  * mess up the original order.
      30             :  */
      31         156 : int elektraKeyCmpOrderWrapper (const void * a, const void * b)
      32             : {
      33         156 :         const Key ** ka = (const Key **) a;
      34         156 :         const Key ** kb = (const Key **) b;
      35             : 
      36         156 :         int orderResult = elektraKeyCmpOrder (*ka, *kb);
      37             : 
      38             :         /* comparing the order meta could not order the keys
      39             :          * revert to comparing the names instead
      40             :          */
      41         156 :         if (orderResult == 0) return keyCmp (*ka, *kb);
      42             : 
      43             :         return orderResult;
      44             : }
      45             : 
      46             : /* The KeySet MUST be sorted alphabetically (or at least ascending
      47             :  * by the length of keynames) for this function to work
      48             :  */
      49          14 : static Key * findNearestParent (Key * key, KeySet * ks)
      50             : {
      51             :         Key * current;
      52          14 :         ksSetCursor (ks, ksGetSize (ks) - 1);
      53          64 :         while ((current = ksPrev (ks)) != 0)
      54             :         {
      55          50 :                 if (keyIsBelow (current, key))
      56             :                 {
      57             :                         return current;
      58             :                 }
      59             :         }
      60             : 
      61             :         return 0;
      62             : }
      63             : 
      64             : /*
      65             :  * Appends a line to the MetaKey of the supplied Key
      66             :  * If no MetaKey with the given name exists yet, a new
      67             :  * one is created containing the supplied line. If
      68             :  * the MetaKey exists, the supplied line is added as
      69             :  * a new line to the value of the MetaKey (i.e. a newline
      70             :  * followed by the given line is appended to the metadata)
      71             :  *
      72             :  * @param       target the Key whose MetaKey is to be modified
      73             :  * @param       metaName the name of the MetaKey which is to be modified
      74             :  * @param       line the line to be appended to the matadata
      75             :  * @return      the new value size of the modified MetaKey
      76             :  * @retval      -1 on NULL pointers or if a memory allocation error occurs
      77             :  *
      78             :  * @see keyGetValueSize(Key *key)
      79             :  *
      80             :  */
      81          50 : int elektraKeyAppendMetaLine (Key * target, const char * metaName, const char * line)
      82             : {
      83          50 :         if (!target) return 0;
      84          50 :         if (!metaName) return 0;
      85          50 :         if (!line) return 0;
      86             : 
      87          50 :         if (!keyGetMeta (target, metaName))
      88             :         {
      89          38 :                 keySetMeta (target, metaName, line);
      90          38 :                 return keyGetValueSize (keyGetMeta (target, metaName));
      91             :         }
      92             : 
      93          12 :         const Key * existingMeta = keyGetMeta (target, metaName);
      94          12 :         char * buffer = elektraMalloc (keyGetValueSize (existingMeta) + strlen (line) + 1);
      95          12 :         if (!buffer) return 0;
      96             : 
      97          12 :         keyGetString (existingMeta, buffer, keyGetValueSize (existingMeta));
      98          12 :         strcat (buffer, "\n");
      99          12 :         strncat (buffer, line, elektraStrLen (line));
     100             : 
     101          12 :         keySetMeta (target, metaName, buffer);
     102          12 :         elektraFree (buffer);
     103          12 :         return keyGetValueSize (keyGetMeta (target, metaName));
     104             : }
     105             : 
     106          50 : static const char * getAppendMode (Key * key)
     107             : {
     108          50 :         const Key * appendModeKey = keyGetMeta (key, CONVERT_APPENDMODE);
     109             :         const char * appendMode;
     110             : 
     111             :         /* append to the next key is the default */
     112          50 :         appendMode = appendModeKey != 0 ? keyString (appendModeKey) : "next";
     113          50 :         return appendMode;
     114             : }
     115             : 
     116          50 : void removeKeyFromResult (Key * convertKey, Key * target, KeySet * orig)
     117             : {
     118             :         /* remember which key this key was converted to
     119             :          * before removing it from the result
     120             :          */
     121          50 :         keySetMeta (convertKey, CONVERT_TARGET, keyName (target));
     122          50 :         Key * key = ksLookup (orig, convertKey, KDB_O_POP);
     123          50 :         keyDel (key);
     124          50 : }
     125             : 
     126         128 : static void flushConvertedKeys (Key * target, KeySet * converted, KeySet * orig)
     127             : {
     128         128 :         if (ksGetSize (converted) == 0) return;
     129             : 
     130          28 :         ksRewind (converted);
     131             :         Key * current;
     132             : 
     133          98 :         while ((current = ksNext (converted)))
     134             :         {
     135          42 :                 Key * appendTarget = target;
     136          42 :                 const char * metaName = keyString (keyGetMeta (current, CONVERT_METANAME));
     137             : 
     138          42 :                 Key * currentDup = keyDup (current);
     139          42 :                 Key * targetDup = keyDup (appendTarget);
     140          42 :                 keySetBaseName (currentDup, 0);
     141          42 :                 keySetBaseName (targetDup, 0);
     142             : 
     143             :                 /* the convert key request to be converted to a key
     144             :                  * on the same level, but the target is below or above
     145             :                  */
     146          42 :                 if (keyGetMeta (current, CONVERT_APPEND_SAMELEVEL) && keyCmp (currentDup, targetDup))
     147             :                 {
     148           6 :                         appendTarget = 0;
     149             :                 }
     150             : 
     151          42 :                 keyDel (currentDup);
     152          42 :                 keyDel (targetDup);
     153             : 
     154             :                 /* no target key was found of the target
     155             :                  * was discarded for some reason. Revert to the parent
     156             :                  */
     157          42 :                 if (!appendTarget)
     158             :                 {
     159           6 :                         appendTarget = findNearestParent (current, orig);
     160             :                 }
     161             : 
     162          42 :                 elektraKeyAppendMetaLine (appendTarget, metaName, keyString (current));
     163          42 :                 removeKeyFromResult (current, target, orig);
     164             :         }
     165             : 
     166          28 :         ksClear (converted);
     167             : }
     168             : 
     169          16 : static KeySet * convertKeys (Key ** keyArray, size_t numKeys, KeySet * orig)
     170             : {
     171          16 :         Key * current = 0;
     172          16 :         Key * prevAppendTarget = 0;
     173          16 :         KeySet * prevConverted = ksNew (0, KS_END);
     174          16 :         KeySet * nextConverted = ksNew (0, KS_END);
     175          16 :         KeySet * result = ksNew (0, KS_END);
     176             : 
     177         114 :         for (size_t index = 0; index < numKeys; index++)
     178             :         {
     179          98 :                 current = keyArray[index];
     180             : 
     181          98 :                 if (!keyGetMeta (current, CONVERT_METANAME))
     182             :                 {
     183             :                         /* flush out "previous" and "next" keys which may have been collected
     184             :                          * because the current key serves as a new border
     185             :                          */
     186          48 :                         ksAppend (result, prevConverted);
     187          48 :                         flushConvertedKeys (prevAppendTarget, prevConverted, orig);
     188          48 :                         prevAppendTarget = current;
     189             : 
     190          48 :                         ksAppend (result, nextConverted);
     191          48 :                         flushConvertedKeys (current, nextConverted, orig);
     192          48 :                         continue;
     193             :                 }
     194             : 
     195          50 :                 const char * appendMode = getAppendMode (current);
     196          50 :                 const char * metaName = keyString (keyGetMeta (current, CONVERT_METANAME));
     197             : 
     198          50 :                 Key * bufferKey = 0;
     199          50 :                 if (!strcmp (appendMode, "previous"))
     200             :                 {
     201          18 :                         ksAppendKey (prevConverted, current);
     202             :                 }
     203             : 
     204          50 :                 if (!strcmp (appendMode, "next"))
     205             :                 {
     206          24 :                         ksAppendKey (nextConverted, current);
     207             :                 }
     208             : 
     209          50 :                 if (!strcmp (appendMode, "parent"))
     210             :                 {
     211           8 :                         Key * parent = findNearestParent (current, orig);
     212           8 :                         elektraKeyAppendMetaLine (parent, metaName, keyString (current));
     213           8 :                         ksAppendKey (result, current);
     214           8 :                         removeKeyFromResult (current, parent, orig);
     215             :                 }
     216             : 
     217             :                 if (bufferKey)
     218             :                 {
     219             :                         keySetString (bufferKey, keyName (current));
     220             :                 }
     221             :         }
     222             : 
     223          16 :         ksAppend (result, prevConverted);
     224          16 :         flushConvertedKeys (prevAppendTarget, prevConverted, orig);
     225             : 
     226          16 :         ksAppend (result, nextConverted);
     227          16 :         flushConvertedKeys (0, nextConverted, orig);
     228             : 
     229          16 :         ksDel (nextConverted);
     230          16 :         ksDel (prevConverted);
     231             : 
     232          16 :         return result;
     233             : }
     234             : 
     235         320 : int elektraKeyToMetaGet (Plugin * handle, KeySet * returned, Key * parentKey ELEKTRA_UNUSED)
     236             : {
     237         320 :         int errnosave = errno;
     238             : 
     239             :         /* configuration only */
     240         320 :         if (!strcmp (keyName (parentKey), "system/elektra/modules/keytometa"))
     241             :         {
     242         304 :                 KeySet * info =
     243             : #include "contract.h"
     244             : 
     245         304 :                         ksAppend (returned, info);
     246         304 :                 ksDel (info);
     247         304 :                 return 1;
     248             :         }
     249             : 
     250          16 :         Key ** keyArray = calloc (ksGetSize (returned), sizeof (Key *));
     251          16 :         int ret = elektraKsToMemArray (returned, keyArray);
     252             : 
     253          16 :         if (ret < 0)
     254             :         {
     255           0 :                 elektraFree (keyArray);
     256           0 :                 ELEKTRA_SET_OUT_OF_MEMORY_ERROR (parentKey, "Memory allocation failed");
     257           0 :                 errno = errnosave;
     258           0 :                 return 0;
     259             :         }
     260             : 
     261          16 :         size_t numKeys = ksGetSize (returned);
     262          16 :         qsort (keyArray, numKeys, sizeof (Key *), elektraKeyCmpOrderWrapper);
     263             : 
     264          16 :         KeySet * convertedKeys = convertKeys (keyArray, numKeys, returned);
     265             : 
     266          16 :         elektraFree (keyArray);
     267             : 
     268             :         /* cleanup what might have been left from a previous call */
     269          16 :         KeySet * old = elektraPluginGetData (handle);
     270          16 :         if (old)
     271             :         {
     272           0 :                 ksDel (old);
     273             :         }
     274             : 
     275          16 :         elektraPluginSetData (handle, convertedKeys);
     276             : 
     277          16 :         errno = errnosave;
     278          16 :         return 1; /* success */
     279             : }
     280             : 
     281             : 
     282           6 : int elektraKeyToMetaSet (Plugin * handle, KeySet * returned, Key * parentKey ELEKTRA_UNUSED)
     283             : {
     284           6 :         KeySet * converted = elektraPluginGetData (handle);
     285             : 
     286             :         /* nothing to do */
     287           6 :         if (converted == 0) return 1;
     288             : 
     289           6 :         ksRewind (converted);
     290             : 
     291           6 :         char * saveptr = 0;
     292           6 :         char * value = 0;
     293             :         Key * current;
     294           6 :         Key * previous = 0;
     295          32 :         while ((current = ksNext (converted)) != 0)
     296             :         {
     297          20 :                 const Key * targetName = keyGetMeta (current, CONVERT_TARGET);
     298          20 :                 const Key * metaName = keyGetMeta (current, CONVERT_METANAME);
     299             : 
     300             :                 /* they should always exist, just to be sure */
     301          20 :                 if (targetName && metaName)
     302             :                 {
     303          20 :                         Key * target = ksLookupByName (returned, keyString (targetName), KDB_O_NONE);
     304             : 
     305             :                         /* this might be NULL as the key might have been deleted */
     306          20 :                         if (target)
     307             :                         {
     308             : 
     309          20 :                                 char * result = 0;
     310          20 :                                 if (target != previous)
     311             :                                 {
     312             :                                         /* handle the first meta line this means initializing strtok and related buffers */
     313          12 :                                         elektraFree (value);
     314          12 :                                         const Key * valueKey = keyGetMeta (target, keyString (metaName));
     315          12 :                                         size_t valueSize = keyGetValueSize (valueKey);
     316          12 :                                         value = elektraMalloc (valueSize);
     317          12 :                                         keyGetString (valueKey, value, valueSize);
     318          12 :                                         keySetMeta (target, keyString (metaName), 0);
     319          12 :                                         result = strtok_r (value, "\n", &saveptr);
     320             :                                 }
     321             :                                 else
     322             :                                 {
     323             :                                         /* just continue splitting the metadata */
     324             :                                         result = strtok_r (NULL, "\n", &saveptr);
     325             :                                 }
     326             : 
     327          20 :                                 keySetString (current, result);
     328             : 
     329          20 :                                 previous = target;
     330             :                         }
     331             :                 }
     332             : 
     333          20 :                 keySetMeta (current, CONVERT_TARGET, 0);
     334          20 :                 keySetMeta (current, CONVERT_METANAME, 0);
     335             : 
     336          20 :                 ksAppendKey (returned, current);
     337             :         }
     338             : 
     339           6 :         elektraFree (value);
     340             : 
     341           6 :         ksDel (converted);
     342           6 :         elektraPluginSetData (handle, 0);
     343             : 
     344           6 :         return 1; /* success */
     345             : }
     346             : 
     347         322 : int elektraKeyToMetaClose (Plugin * handle, Key * errorKey ELEKTRA_UNUSED)
     348             : {
     349         322 :         KeySet * old = elektraPluginGetData (handle);
     350             : 
     351         322 :         if (old)
     352             :         {
     353          10 :                 ksDel (old);
     354             :         }
     355             : 
     356         322 :         return 1;
     357             : }
     358             : 
     359         322 : Plugin * ELEKTRA_PLUGIN_EXPORT
     360             : {
     361             :         // clang-format off
     362         322 :         return elektraPluginExport("keytometa",
     363             :                 ELEKTRA_PLUGIN_GET,     &elektraKeyToMetaGet,
     364             :                 ELEKTRA_PLUGIN_SET,     &elektraKeyToMetaSet,
     365             :                 ELEKTRA_PLUGIN_CLOSE, &elektraKeyToMetaClose,
     366             :                 ELEKTRA_PLUGIN_END);
     367             : }
     368             : 

Generated by: LCOV version 1.13