LCOV - code coverage report
Current view: top level - src/libs/proposal - proposal.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 101 104 97.1 %
Date: 2019-09-12 12:28:41 Functions: 9 10 90.0 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief Implementation of proposed API enhancements.
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  *
       8             :  */
       9             : 
      10             : #include <ctype.h>
      11             : #include <string.h>
      12             : 
      13             : #include <kdbassert.h>
      14             : #include <kdblogger.h>
      15             : #include <kdbprivate.h>
      16             : 
      17             : /**
      18             :  * @defgroup proposal Proposals for Elektra
      19             :  * @brief Might be added to, changed or removed from future Elektra releases.
      20             :  */
      21             : 
      22             : /**
      23             :  * @defgroup api API Proposals for Elektra
      24             :  * @brief for kdb.h.
      25             :  * @ingroup proposal
      26             :  *
      27             :  * @warning Do not use these methods if you do not want to depend on
      28             :  * exactly the Elektra version your binary was built for.
      29             :  *
      30             :  * These methods are a technical preview of what might be added in
      31             :  * future Elektra releases. It is a requirement that methods are first
      32             :  * added here, before they are added to the public API.
      33             :  *
      34             :  * Usually, names in proposal stage should be prefixed with elektra to
      35             :  * clearly mark that the signature is likely to be changed and not yet
      36             :  * ABI compatible.
      37             :  *
      38             :  * @{
      39             :  */
      40             : 
      41             : 
      42             : /**
      43             :  * @brief Set a formatted string
      44             :  *
      45             :  * @param key the key to set the string value
      46             :  * @param format NULL-terminated text format string
      47             :  * @param ... more arguments
      48             :  *
      49             :  * @return the size of the string as set (with including 0)
      50             :  */
      51          26 : ssize_t keySetStringF (Key * key, const char * format, ...)
      52             : {
      53             :         va_list arg_list;
      54             : 
      55          26 :         keySetMeta (key, "binary", 0);
      56             : 
      57          26 :         va_start (arg_list, format);
      58          26 :         char * p = elektraVFormat (format, arg_list);
      59          26 :         va_end (arg_list);
      60             : 
      61          26 :         if (!p)
      62             :         {
      63             :                 return -1;
      64             :         }
      65             : 
      66          26 :         if (key->data.c && !test_bit (key->flags, KEY_FLAG_MMAP_DATA))
      67             :         {
      68          22 :                 elektraFree (key->data.c);
      69             :         }
      70             : 
      71          26 :         key->data.c = p;
      72          26 :         key->dataSize = elektraStrLen (key->data.c);
      73          26 :         set_bit (key->flags, KEY_FLAG_SYNC);
      74             : 
      75          26 :         return key->dataSize;
      76             : }
      77             : 
      78             : 
      79             : /**
      80             :  * Builds an array of pointers to the keys in the supplied keyset.
      81             :  * The keys are not copied, calling keyDel may remove them from
      82             :  * the keyset.
      83             :  *
      84             :  * The size of the buffer can be easily allocated via ksGetSize. Example:
      85             :  * @code
      86             :  * KeySet *ks = somekeyset;
      87             :  * Key **keyArray = calloc (ksGetSize(ks), sizeof (Key *));
      88             :  * elektraKsToMemArray (ks, keyArray);
      89             :  * ... work with the array ...
      90             :  * elektraFree (keyArray);
      91             :  * @endcode
      92             :  *
      93             :  * @param ks the keyset object to work with
      94             :  * @param buffer the buffer to put the result into
      95             :  * @return the number of elements in the array if successful
      96             :  * @return a negative number on null pointers or if an error occurred
      97             :  */
      98        3792 : int elektraKsToMemArray (KeySet * ks, Key ** buffer)
      99             : {
     100        3792 :         if (!ks) return -1;
     101        3790 :         if (!buffer) return -1;
     102             : 
     103             :         /* clear the received buffer */
     104        3788 :         memset (buffer, 0, ksGetSize (ks) * sizeof (Key *));
     105             : 
     106        3788 :         cursor_t cursor = ksGetCursor (ks);
     107        3788 :         ksRewind (ks);
     108        3788 :         size_t idx = 0;
     109             : 
     110             :         Key * key;
     111       32087 :         while ((key = ksNext (ks)) != 0)
     112             :         {
     113       24511 :                 buffer[idx] = key;
     114       24511 :                 ++idx;
     115             :         }
     116        3788 :         ksSetCursor (ks, cursor);
     117             : 
     118        3788 :         return idx;
     119             : }
     120             : 
     121             : /**
     122             :  * @brief Takes the first key and cuts off this common part
     123             :  * for all other keys, instead name will be prepended
     124             :  *
     125             :  * @return a new allocated keyset with keys in user namespace.
     126             :  *
     127             :  * The first key is removed in the resulting keyset.
     128             :  */
     129           0 : KeySet * ksRenameKeys (KeySet * config, const Key * name)
     130             : {
     131           0 :         return elektraRenameKeys (config, keyName (name));
     132             : }
     133             : 
     134             : /**
     135             :  * @brief Permanently locks a part of the key
     136             :  *
     137             :  * This can be:
     138             :  * - KEY_FLAG_LOCK_NAME to lock the name
     139             :  * - KEY_FLAG_LOCK_VALUE to lock the value
     140             :  * - KEY_FLAG_LOCK_META to lock the metadata
     141             :  *
     142             :  * To unlock the key, duplicate it.
     143             :  *
     144             :  * It is also possible to lock when the key is created with
     145             :  * keyNew().
     146             :  *
     147             :  * Some data structures need to lock the key (most likely
     148             :  * its name), so that the ordering does not get confused.
     149             :  *
     150             :  * @param key which name should be locked
     151             :  *
     152             :  * @see keyNew(), keyDup(), ksAppendKey()
     153             :  * @retval >0 the bits that were successfully locked
     154             :  * @retval 0 if everything was locked before
     155             :  * @retval -1 if it could not be locked (nullpointer)
     156             :  */
     157           6 : int keyLock (Key * key, option_t what)
     158             : {
     159           6 :         return elektraKeyLock (key, what);
     160             : }
     161             : 
     162             : 
     163             : /**
     164             :  * @brief Return metadata as keyset
     165             :  *
     166             :  * @param key the key object to work with
     167             :  *
     168             :  * @return a duplication of the keyset representing the metadata
     169             :  */
     170          58 : KeySet * elektraKeyGetMetaKeySet (const Key * key)
     171             : {
     172          58 :         if (!key) return 0;
     173          58 :         if (!key->meta) return 0;
     174             : 
     175          31 :         return ksDup (key->meta);
     176             : }
     177             : 
     178             : 
     179             : /**
     180             :  * Returns the previous Key in a KeySet.
     181             :  *
     182             :  * KeySets have an internal cursor that can be reset with ksRewind(). Every
     183             :  * time ksPrev() is called the cursor is decremented and the new current Key
     184             :  * is returned.
     185             :  *
     186             :  * You'll get a NULL pointer if the key before begin of the KeySet was reached.
     187             :  *
     188             :  * Don't delete the key, use ksPop() if you want to delete it.
     189             :  *
     190             :  * @return the new current Key
     191             :  * @see ksRewind(), ksCurrent()
     192             :  *
     193             :  */
     194          50 : Key * ksPrev (KeySet * ks)
     195             : {
     196          50 :         return elektraKsPrev (ks);
     197             : }
     198             : 
     199             : /**
     200             :  * @brief Pop key at given cursor position
     201             :  *
     202             :  * @param ks the keyset to pop key from
     203             :  * @param c where to pop
     204             :  *
     205             :  * The internal cursor will be rewinded using ksRewind(). You can use
     206             :  * ksGetCursor() and ksSetCursor() jump back to the previous position.
     207             :  * e.g. to pop at current position within ksNext() loop:
     208             :  * @code
     209             :  * cursor_t c = ksGetCursor(ks);
     210             :  * keyDel (ksPopAtCursor(ks, c));
     211             :  * ksSetCursor(ks, c);
     212             :  * ksPrev(ks); // to have correct key after next ksNext()
     213             :  * @endcode
     214             :  *
     215             :  * @warning do not use, will be superseded by external iterator API
     216             :  *
     217             :  * @return the popped key
     218             :  * @retval 0 if ks is 0
     219             :  */
     220          10 : Key * ksPopAtCursor (KeySet * ks, cursor_t pos)
     221             : {
     222          10 :         return elektraKsPopAtCursor (ks, pos);
     223             : }
     224             : 
     225             : 
     226             : /**
     227             :  * keyRel replacement
     228             :  */
     229             : 
     230             : // keyRel2 helper, turns key into a cascading key ( removes namespace)
     231         172 : Key * keyAsCascading (const Key * key)
     232             : {
     233         172 :         if (keyName (key)[0] == '/')
     234             :         {
     235          26 :                 return keyDup (key);
     236             :         }
     237             :         else
     238             :         {
     239         146 :                 elektraNamespace ns = keyGetNamespace (key);
     240         146 :                 if (ns == KEY_NS_META || ns == KEY_NS_EMPTY || ns == KEY_NS_NONE)
     241             :                 {
     242             :                         // For metakeys or keys without namespace just prefix the keyname with a "/"
     243          32 :                         Key * cKey = keyNew ("/", KEY_CASCADING_NAME, KEY_END);
     244          32 :                         keyAddName (cKey, keyName (key));
     245          32 :                         return cKey;
     246             :                 }
     247             :                 else
     248             :                 {
     249             :                         // Skip namespace
     250         114 :                         const char * name = keyName (key);
     251         114 :                         const char * ptr = strchr (name, '/');
     252         114 :                         if (!ptr)
     253             :                         {
     254           4 :                                 return keyNew ("/", KEY_CASCADING_NAME, KEY_END);
     255             :                         }
     256             :                         else
     257             :                         {
     258         110 :                                 ssize_t length = keyGetNameSize (key);
     259         110 :                                 if ((ptr - name) == (length - 1))
     260             :                                 {
     261           0 :                                         return keyNew ("/", KEY_CASCADING_NAME, KEY_END);
     262             :                                 }
     263             :                                 else
     264             :                                 {
     265         110 :                                         return keyNew (ptr, KEY_CASCADING_NAME, KEY_END);
     266             :                                 }
     267             :                         }
     268             :                 }
     269             :         }
     270             : }
     271             : 
     272             : // keyRel2 helper, returns how many levels check is below key, or 0 if check isn't below
     273         144 : int keyGetLevelsBelow (const Key * key, const Key * check)
     274             : {
     275         144 :         const char * keyUName = keyUnescapedName (key);
     276         144 :         size_t keyUSize = keyGetUnescapedNameSize (key);
     277         144 :         const char * keyUNameEnd = keyUName + keyUSize;
     278             : 
     279         144 :         const char * checkUName = keyUnescapedName (check);
     280         144 :         size_t checkUSize = keyGetUnescapedNameSize (check);
     281         144 :         const char * checkUNameEnd = checkUName + checkUSize;
     282             : 
     283         392 :         while (strcmp (keyUName, checkUName) == 0)
     284             :         {
     285         204 :                 keyUName = strchr (keyUName, '\0') + 1;
     286         204 :                 checkUName = strchr (checkUName, '\0') + 1;
     287             : 
     288         204 :                 if (keyUName >= keyUNameEnd)
     289             :                 {
     290             :                         int levels = 0;
     291         246 :                         while (checkUName < checkUNameEnd)
     292             :                         {
     293         150 :                                 checkUName = strchr (checkUName, '\0') + 1;
     294         150 :                                 ++levels;
     295             :                         }
     296             : 
     297             :                         return levels;
     298             :                 }
     299             : 
     300         108 :                 if (checkUName >= checkUNameEnd)
     301             :                 {
     302             :                         break;
     303             :                 }
     304             :         }
     305             : 
     306             :         return 0;
     307             : }
     308             : 
     309             : /**
     310             :  * @brief Replacement proposal for keyRel
     311             :  * @return depending on relation type
     312             :  * @retval -1 usage error
     313             :  * @retval 0 test failed
     314             :  * @retval >1 true for binary tests, number of levels below for other relation tests
     315             :  *
     316             :  * @param key the key object to work with
     317             :  * @param check the second key object to check the relation with
     318             :  * @param which what kind of relationship test should be done
     319             :  */
     320             : 
     321             : 
     322          82 : int keyRel2 (const Key * key, const Key * check, KeyRelType which)
     323             : {
     324          82 :         if (!key || !check) return -1;
     325          82 :         if (!key->key || !check->key) return -1;
     326             : 
     327          82 :         Key * cKey = keyAsCascading (key);
     328          82 :         Key * cCheck = keyAsCascading (check);
     329          82 :         Key * cKeyParent = keyDup (cKey);
     330          82 :         keySetBaseName (cKeyParent, 0);
     331          82 :         if (keyName (cKeyParent)[0] == '\0') keySetName (cKeyParent, "/");
     332          82 :         int isBelow = 0;
     333          82 :         int isSilblingNephew = 0;
     334          82 :         isBelow = keyGetLevelsBelow (cKey, cCheck);
     335          82 :         if (!isBelow) isSilblingNephew = keyGetLevelsBelow (cKeyParent, cCheck);
     336          82 :         elektraNamespace keyNamespace = keyGetNamespace (key);
     337          82 :         elektraNamespace checkNamespace = keyGetNamespace (check);
     338          82 :         int retVal = 0;
     339          82 :         int bits = 0;
     340        2706 :         for (KeyRelType type = 1; type != 0; type <<= 1)
     341             :         {
     342        2624 :                 if (type & which) ++bits;
     343             :         }
     344          82 :         if (bits != 1) return -1;
     345          82 :         switch (which)
     346             :         {
     347             :         case ELEKTRA_REL_BELOW_SAME_NS:
     348          12 :                 if (isBelow && (keyNamespace == checkNamespace)) retVal = isBelow;
     349             :                 break;
     350             :         case ELEKTRA_REL_BELOW_IGNORE_NS:
     351          12 :                 if (isBelow) retVal = isBelow;
     352             :                 break;
     353             :         case ELEKTRA_REL_BELOW_CASCADING_NS:
     354          10 :                 if (isBelow && ((checkNamespace == KEY_NS_CASCADING) || (keyNamespace == KEY_NS_CASCADING))) retVal = isBelow;
     355             :                 break;
     356             :         case ELEKTRA_REL_DIRECT_BELOW_SAME_NS:
     357           6 :                 if ((isBelow == 1) && (keyNamespace == checkNamespace)) retVal = 1;
     358             :                 break;
     359             :         case ELEKTRA_REL_DIRECT_BELOW_IGNORE_NS:
     360           2 :                 if (isBelow == 1) retVal = 1;
     361             :                 break;
     362             :         case ELEKTRA_REL_DIRECT_BELOW_CASCADING_NS:
     363           2 :                 if ((isBelow == 1) && ((checkNamespace == KEY_NS_CASCADING) || (keyNamespace == KEY_NS_CASCADING))) retVal = 1;
     364             :                 break;
     365             :         case ELEKTRA_REL_SILBLING_SAME_NS:
     366           8 :                 if ((isSilblingNephew == 1) && (keyNamespace == checkNamespace)) retVal = 1;
     367             :                 break;
     368             :         case ELEKTRA_REL_SILBLING_IGNORE_NS:
     369           4 :                 if (isSilblingNephew == 1) retVal = 1;
     370             :                 break;
     371             :         case ELEKTRA_REL_SILBLING_CASCADING_NS:
     372           4 :                 if ((isSilblingNephew == 1) && ((checkNamespace == KEY_NS_CASCADING) || (keyNamespace == KEY_NS_CASCADING))) retVal = 1;
     373             :                 break;
     374             :         case ELEKTRA_REL_NEPHEW_SAME_NS:
     375          14 :                 if ((isSilblingNephew > 1) && (keyNamespace == checkNamespace)) retVal = isSilblingNephew - 1;
     376             :                 break;
     377             :         case ELEKTRA_REL_NEPHEW_IGNORE_NS:
     378           4 :                 if (isSilblingNephew > 1) retVal = isSilblingNephew - 1;
     379             :                 break;
     380             :         case ELEKTRA_REL_NEPHEW_CASCADING_NS:
     381           4 :                 if ((isSilblingNephew > 1) && ((checkNamespace == KEY_NS_CASCADING) || (keyNamespace == KEY_NS_CASCADING)))
     382           4 :                         retVal = isSilblingNephew - 1;
     383             :                 break;
     384             :         default:
     385             :                 retVal = -1;
     386             :                 break;
     387             :         }
     388          82 :         keyDel (cKey);
     389          82 :         keyDel (cCheck);
     390          82 :         keyDel (cKeyParent);
     391          82 :         return retVal;
     392             : }
     393             : 
     394             : /**
     395             :  * @}
     396             :  */

Generated by: LCOV version 1.13