LCOV - code coverage report
Current view: top level - src/libs/elektra - keytest.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 125 132 94.7 %
Date: 2019-09-12 12:28:41 Functions: 15 16 93.8 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief Methods for making tests
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  */
       8             : 
       9             : 
      10             : #ifdef HAVE_KDBCONFIG_H
      11             : #include "kdbconfig.h"
      12             : #endif
      13             : 
      14             : #ifdef HAVE_STDARG_H
      15             : #include <stdarg.h>
      16             : #endif
      17             : 
      18             : #ifdef HAVE_STRING_H
      19             : #include <string.h>
      20             : #endif
      21             : 
      22             : #ifdef HAVE_STDLIB_H
      23             : #include <stdlib.h>
      24             : #endif
      25             : 
      26             : #include "kdb.h"
      27             : #include "kdbinternal.h"
      28             : #include "kdbprivate.h"
      29             : 
      30             : 
      31             : /**
      32             :  * @defgroup keytest Methods for Making Tests
      33             :  * @ingroup key
      34             :  * @brief Methods to do various tests on Keys
      35             :  *
      36             :  * To use them:
      37             :  * @code
      38             : #include <kdb.h>
      39             :  * @endcode
      40             :  *
      41             :  *
      42             :  */
      43             : 
      44             : /**
      45             :  * @internal
      46             :  *
      47             :  * Clear sync flag of a key.
      48             :  *
      49             :  * @param key the key object to work with
      50             :  * @retval -1 on null key
      51             :  * @return new flags for that key otherwise
      52             :  * @ingroup keytest
      53             :  *
      54             :  */
      55      317375 : int keyClearSync (Key * key)
      56             : {
      57      317375 :         if (!key) return -1;
      58             : 
      59      317375 :         keyflag_t semiflag = KEY_FLAG_SYNC;
      60             : 
      61      317375 :         semiflag = ~semiflag;
      62      317375 :         key->flags &= semiflag;
      63             : 
      64      317375 :         return key->flags;
      65             : }
      66             : 
      67             : 
      68             : /**
      69             :  * Test if a key needs to be synced to backend storage.
      70             :  *
      71             :  * If any key modification took place the key will be flagged
      72             :  * so that kdbSet() knows which keys were modified
      73             :  * and which not.
      74             :  *
      75             :  * After keyNew() the flag will normally be set, but after kdbGet()
      76             :  * and kdbSet() the flag will be removed. When you modify the key
      77             :  * the flag will be set again.
      78             :  *
      79             :  * In your application you can make use of that flag to know
      80             :  * if you changed something in a key after a kdbGet() or kdbSet().
      81             :  *
      82             :  * @note Note that the sync status will be updated on any change,
      83             :  * including metadata.
      84             :  *
      85             :  * @deprecated The handling of synchronization is done internally and
      86             :  * does not need to be checked by neither application nor plugins.
      87             :  *
      88             :  * @see after keyNew(), keyDup() keys need sync
      89             :  *
      90             :  * @param key the key object to work with
      91             :  * @retval 1 if @p key was changed in memory, 0 otherwise
      92             :  * @retval -1 on NULL pointer
      93             :  * @ingroup keytest
      94             :  */
      95      161456 : int keyNeedSync (const Key * key)
      96             : {
      97      161456 :         if (!key) return -1;
      98             : 
      99      161446 :         return (key->flags & KEY_FLAG_SYNC) == KEY_FLAG_SYNC;
     100             : }
     101             : 
     102             : 
     103         711 : int keyIsSpec (const Key * key)
     104             : {
     105         711 :         if (!key) return -1;
     106             : 
     107         711 :         if (key->key)
     108         711 :                 return keyNameIsSpec (key->key);
     109             :         else
     110             :                 return 0;
     111             : }
     112             : 
     113           0 : int keyIsProc (const Key * key)
     114             : {
     115           0 :         if (!key) return -1;
     116             : 
     117           0 :         if (key->key)
     118           0 :                 return keyNameIsProc (key->key);
     119             :         else
     120             :                 return 0;
     121             : }
     122             : 
     123             : 
     124         334 : int keyIsDir (const Key * key)
     125             : {
     126         334 :         if (!key) return -1;
     127             : 
     128         334 :         if (key->key)
     129         334 :                 return keyNameIsDir (key->key);
     130             :         else
     131             :                 return 0;
     132             : }
     133             : 
     134             : 
     135             : /**
     136             :  * @internal
     137             :  *
     138             :  * Check whether a key is under the @p system namespace or not
     139             :  *
     140             :  * @param key the key object to work with
     141             :  * @retval 1 if key name begins with @p system, 0 otherwise
     142             :  * @retval -1 on NULL pointer
     143             :  * @see keyIsUser(), keySetName(), keyName()
     144             :  * @ingroup keytest
     145             :  *
     146             :  */
     147     1228631 : int keyIsSystem (const Key * key)
     148             : {
     149     1228631 :         if (!key) return -1;
     150             : 
     151     1228629 :         if (key->key)
     152     1228627 :                 return keyNameIsSystem (key->key);
     153             :         else
     154             :                 return 0;
     155             : }
     156             : 
     157             : 
     158             : /**
     159             :  * @internal
     160             :  *
     161             :  * Check whether a key is under the @p user namespace or not.
     162             :  *
     163             :  * @param key the key object to work with
     164             :  * @retval 1 if key name begins with @p user, 0 otherwise
     165             :  * @retval -1 on NULL pointer
     166             :  * @see keyIsSystem(), keySetName(), keyName()
     167             :  * @ingroup keytest
     168             :  *
     169             :  */
     170     1128608 : int keyIsUser (const Key * key)
     171             : {
     172     1128608 :         if (!key) return -1;
     173             : 
     174     1128606 :         if (key->key)
     175     1128604 :                 return keyNameIsUser (key->key);
     176             :         else
     177             :                 return 0;
     178             : }
     179             : 
     180             : /**
     181             :  * Check if the key check is below the key key or not.
     182             :  *
     183             :  * Example:
     184             :  @verbatim
     185             :  key user/sw/app
     186             :  check user/sw/app/key
     187             :  @endverbatim
     188             :  *
     189             :  * returns true because check is below key
     190             :  *
     191             :  * Example:
     192             :  @verbatim
     193             :  key user/sw/app
     194             :  check user/sw/app/folder/key
     195             :  @endverbatim
     196             :  *
     197             :  * returns also true because check is indirect below key
     198             :  *
     199             :  * Obviously, there is no key above a namespace (e.g. user, system, /):
     200             :  *
     201             :  @verbatim
     202             :  key *
     203             :  check user
     204             :  @endverbatim
     205             :  *
     206             :  * @param key the key object to work with
     207             :  * @param check the key to find the relative position of
     208             :  * @retval 1 if check is below key
     209             :  * @retval 0 if it is not below or if it is the same key
     210             :  * @retval -1 if key or check is null
     211             :  * @see keySetName(), keyGetName(), keyIsDirectBelow()
     212             :  * @ingroup keytest
     213             :  *
     214             :  */
     215             : 
     216     1143842 : int keyIsBelow (const Key * key, const Key * check)
     217             : {
     218     1143842 :         if (key == NULL || check == NULL)
     219             :         {
     220             :                 return -1;
     221             :         }
     222             : 
     223             :         // same key, only if namespace and size are equal
     224             :         // size alone could be equal with cascading keys
     225     1486394 :         return keyIsBelowOrSame (key, check) &&
     226      684848 :                (keyGetNamespace (key) != keyGetNamespace (check) || keyGetUnescapedNameSize (key) != keyGetUnescapedNameSize (check));
     227             : }
     228             : 
     229             : 
     230             : /**
     231             :  * Check if a key is below or same or not.
     232             :  *
     233             :  * @param key the key object to work with
     234             :  * @see keyIsBelow()
     235             :  */
     236     2536442 : int keyIsBelowOrSame (const Key * key, const Key * check)
     237             : {
     238     2536442 :         if (key == NULL || check == NULL)
     239             :         {
     240             :                 return -1;
     241             :         }
     242             : 
     243     2536436 :         const char * above = keyUnescapedName (key);
     244     2536436 :         const char * below = keyUnescapedName (check);
     245             : 
     246     2536436 :         size_t sizeAbove = keyGetUnescapedNameSize (key);
     247     2536436 :         size_t sizeBelow = keyGetUnescapedNameSize (check);
     248             : 
     249     2536436 :         if (sizeAbove == 1 && above[0] == '\0' && below[0] != '\0' && sizeBelow == strlen (below) + 1)
     250             :         {
     251             :                 // cascading root compared against other root
     252             :                 return 0;
     253             :         }
     254             : 
     255     2536432 :         if (sizeBelow == 1 && below[0] == '\0' && above[0] != '\0' && sizeAbove == strlen (above) + 1)
     256             :         {
     257             :                 // cascading root compared against other root
     258             :                 return 0;
     259             :         }
     260             : 
     261     2536428 :         if (above[0] != '\0' && below[0] == '\0')
     262             :         {
     263             :                 // cascading
     264         642 :                 size_t len = strlen (above);
     265         642 :                 above += len;
     266         642 :                 sizeAbove -= len;
     267             :         }
     268             : 
     269     2536428 :         if (below[0] != '\0' && above[0] == '\0')
     270             :         {
     271             :                 // cascading
     272        6658 :                 size_t len = strlen (below);
     273        6658 :                 below += len;
     274        6658 :                 sizeBelow -= len;
     275             :         }
     276             : 
     277     2536428 :         if (sizeAbove > sizeBelow)
     278             :         {
     279             :                 return 0;
     280             :         }
     281             : 
     282     2003858 :         return memcmp (above, below, sizeAbove) == 0;
     283             : }
     284             : 
     285             : 
     286             : /**
     287             :  * Check if the key check is direct below the key key or not.
     288             :  *
     289             :  @verbatim
     290             : Example:
     291             : key user/sw/app
     292             : check user/sw/app/key
     293             : 
     294             : returns true because check is below key
     295             : 
     296             : Example:
     297             : key user/sw/app
     298             : check user/sw/app/folder/key
     299             : 
     300             : does not return true, because there is only an indirect relation
     301             : @endverbatim
     302             :  *
     303             :  * @param key the key object to work with
     304             :  * @param check the key to find the relative position of
     305             :  * @retval 1 if check is below key
     306             :  * @retval 0 if it is not below or if it is the same key
     307             :  * @retval -1 on null pointer
     308             :  * @see keyIsBelow(), keySetName(), keyGetName()
     309             :  * @ingroup keytest
     310             :  *
     311             :  */
     312     1114163 : int keyIsDirectBelow (const Key * key, const Key * check)
     313             : {
     314     1114163 :         if (key == NULL || check == NULL)
     315             :         {
     316             :                 return -1;
     317             :         }
     318             : 
     319     1114157 :         const char * above = keyUnescapedName (key);
     320     1114157 :         const char * below = keyUnescapedName (check);
     321             : 
     322     1114157 :         size_t sizeAbove = keyGetUnescapedNameSize (key);
     323     1114157 :         size_t sizeBelow = keyGetUnescapedNameSize (check);
     324             : 
     325     1114157 :         if (above[0] != '\0' && below[0] == '\0')
     326             :         {
     327             :                 // cascading
     328         634 :                 size_t len = strlen (above);
     329         634 :                 above += len;
     330         634 :                 sizeAbove -= len;
     331             :         }
     332             : 
     333     1114157 :         if (below[0] != '\0' && above[0] == '\0')
     334             :         {
     335             :                 // cascading
     336        1223 :                 size_t len = strlen (below);
     337        1223 :                 below += len;
     338        1223 :                 sizeBelow -= len;
     339             :         }
     340             : 
     341     1114157 :         if (sizeAbove >= sizeBelow)
     342             :         {
     343             :                 return 0;
     344             :         }
     345             : 
     346      689283 :         size_t nextPartSize = strlen (below + sizeAbove);
     347      689283 :         return memcmp (above, below, sizeAbove) == 0 && sizeAbove + nextPartSize + 1 == sizeBelow;
     348             : }
     349             : 
     350             : 
     351             : /**
     352             :  * Information about the relation in the hierarchy between
     353             :  * two keys.
     354             :  *
     355             :  * Unlike keyCmp() the number gives information
     356             :  * about hierarchical information.
     357             :  *
     358             :  *
     359             :  * - If the keys are the same 0 is returned.
     360             :  * So it is the key itself.
     361             :  @verbatim
     362             :  user/key
     363             :  user/key
     364             :  @endverbatim
     365             :  *
     366             :  *@code
     367             :  keySetName (key, "user/key/folder");
     368             :  keySetName (check, "user/key/folder");
     369             :  succeed_if (keyRel (key, check) == 0, "should be same");
     370             :  *@endcode
     371             :  *
     372             :  * @note this relation can be checked with keyCmp() too.
     373             :  *
     374             :  *
     375             :  * - If the key is direct below the other one 1 is returned.
     376             :  * That means that, in terms of hierarchy, no other key is
     377             :  * between them - it is a direct child.
     378             :  @verbatim
     379             :  user/key/folder
     380             :  user/key/folder/child
     381             :  @endverbatim
     382             :  *
     383             :  *@code
     384             :  keySetName (key, "user/key/folder");
     385             :  keySetName (check, "user/key/folder/child");
     386             :  succeed_if (keyRel (key, check) == 1, "should be direct below");
     387             :  *@endcode
     388             :  *
     389             :  *
     390             :  * - If the key is below the other one, but not directly 2 is returned.
     391             :  * This is also called grand-child.
     392             :  @verbatim
     393             :  user/key/folder
     394             :  user/key/folder/any/depth/deeper/grand-child
     395             :  @endverbatim
     396             :  *
     397             :  *
     398             :  *@code
     399             :  keySetName (key, "user/key/folder");
     400             :  keySetName (check, "user/key/folder/any/depth/deeper/grand-child");
     401             :  succeed_if (keyRel (key, check) >= 2, "should be below (but not direct)");
     402             :  succeed_if (keyRel (key, check) > 0, "should be below");
     403             :  succeed_if (keyRel (key, check) >= 0, "should be the same or below");
     404             :  *@endcode
     405             :  *
     406             :  *
     407             :  * - If an invalid or null ptr key is passed, -1 is returned
     408             :  *
     409             :  *
     410             :  * - If the keys have no relations, but are not invalid, -2 is returned.
     411             :  *
     412             :  *
     413             :  * - If the keys are in the same hierarchy, a value smaller then -2 is returned.
     414             :  * It means that the key is not below.
     415             :  @verbatim
     416             :  user/key/myself
     417             :  user/key/sibling
     418             :  @endverbatim
     419             :  *
     420             :  * @code
     421             :  keySetName (key, "user/key/folder");
     422             :  keySetName (check, "user/notsame/folder");
     423             :  succeed_if (keyRel (key, check) < -2, "key is not below, but same namespace");
     424             :  * @endcode
     425             :  *
     426             :  * @code
     427             :  * @endcode
     428             :  *
     429             :  *
     430             :  * TODO Below is an idea how it could be extended:
     431             :  * It could continue the search into the other direction
     432             :  * if any (grand-)parents are equal.
     433             :  *
     434             :  * - If the keys are direct below a key which is next to the key, -2 is returned.
     435             :  * This is also called nephew. (TODO not implemented)
     436             :  * @verbatim
     437             :  user/key/myself
     438             :  user/key/sibling
     439             :  @endverbatim
     440             :  *
     441             :  * - If the keys are direct below a key which is next to the key, -2 is returned.
     442             :  * This is also called nephew. (TODO not implemented)
     443             :  * @verbatim
     444             :  user/key/myself
     445             :  user/key/sibling/nephew
     446             :  @endverbatim
     447             :  *
     448             :  * - If the keys are below a key which is next to the key, -3 is returned.
     449             :  * This is also called grand-nephew. (TODO not implemented)
     450             :  @verbatim
     451             :  user/key/myself
     452             :  user/key/sibling/any/depth/deeper/grand-nephew
     453             :  @endverbatim
     454             :  *
     455             :  * The same holds true for the other direction, but with negative values.
     456             :  * For no relation INT_MIN is returned.
     457             :  *
     458             :  * @note to check if the keys are the same, you must use
     459             :  *              keyCmp() == 0!
     460             :  *              keyRel() does not give you the information if it did not
     461             :  *              find a relation or if it is the same key.
     462             :  *
     463             :  * @return depending on the relation
     464             :  * @retval 2 if below
     465             :  * @retval 1 if direct below
     466             :  * @retval 0 if the same
     467             :  * @retval -1 on null or invalid keys
     468             :  * @retval -2 if none of any other relation
     469             :  * @retval -3 if same hierarchy (none of those below)
     470             :  * @retval -4 if sibling (in same hierarchy)
     471             :  * @retval -5 if nephew (in same hierarchy)
     472             :  *
     473             :  * @param key the key object to work with
     474             :  * @param check the second key object to check the relation with
     475             :  * @ingroup keytest
     476             :  */
     477     1225811 : int keyRel (const Key * key, const Key * check)
     478             : {
     479     1225811 :         if (!key || !check) return -1;
     480     1225807 :         if (!key->key || !check->key) return -1;
     481             : 
     482     1225803 :         if (!keyCmp (key, check)) return 0;
     483     1077266 :         if (keyIsDirectBelow (key, check)) return 1;
     484      839381 :         if (keyIsBelow (key, check)) return 2;
     485      782887 :         if (keyIsUser (key) && keyIsUser (check)) return -3;
     486      774792 :         if (keyIsSystem (key) && keyIsSystem (check)) return -3;
     487             :         // if (keyIsSibling(key, check)) return -4;
     488             :         // if (keyIsNephew(key, check)) return -5;
     489             : 
     490             :         return -2;
     491             : }
     492             : 
     493             : 
     494             : /**
     495             :  * Check whether a key is inactive.
     496             :  *
     497             :  * In Elektra terminology a hierarchy of keys is inactive if
     498             :  * the rootkey's basename starts with '.'. So a key is
     499             :  * also inactive if it is below an inactive key.
     500             :  * For example, user/key/.hidden is inactive and so
     501             :  * is user/.hidden/below.
     502             :  *
     503             :  * Inactive keys should not have any meaning to applications,
     504             :  * they are only a convention reserved for users and
     505             :  * administrators. To automatically remove all inactive keys
     506             :  * for an application, consider to use the hidden plugin.
     507             :  *
     508             :  * @param key the key object to work with
     509             :  * @retval 1 if the key is inactive
     510             :  * @retval 0 if the key is active
     511             :  * @retval -1 on NULL pointer or when key has no name
     512             :  * @ingroup keytest
     513             :  *
     514             :  */
     515          48 : int keyIsInactive (const Key * key)
     516             : {
     517          48 :         if (!key) return -1;
     518             : 
     519          46 :         const char * p = keyName (key);
     520          46 :         if (!p) return -1;
     521          46 :         if (p[0] == '\0') return -1;
     522             : 
     523          44 :         size_t size = 0;
     524             : 
     525         173 :         while (*(p = keyNameGetOneLevel (p + size, &size)))
     526             :         {
     527         116 :                 if (size > 0)
     528             :                 {
     529         116 :                         if (p[0] == '.')
     530             :                         {
     531             :                                 return 1;
     532             :                         }
     533             :                 }
     534             :         }
     535             : 
     536             :         return 0;
     537             : }
     538             : 
     539             : 
     540             : /**
     541             :  * Check if a key is binary type.
     542             :  *
     543             :  * The function checks if the key is a binary. Opposed to string values binary
     544             :  * values can have '\\0' inside the value and may not be terminated by a null
     545             :  * character. Their disadvantage is that you need to pass their size.
     546             :  *
     547             :  * Make sure to use this function and don't test the binary type another way to
     548             :  * ensure compatibility and to write less error prone programs.
     549             :  *
     550             :  * @retval 1 if it is binary
     551             :  * @retval 0 if it is not
     552             :  * @retval -1 on NULL pointer
     553             :  * @see keyGetBinary(), keySetBinary()
     554             :  * @param key the key to check
     555             :  * @ingroup keytest
     556             :  */
     557     2755749 : int keyIsBinary (const Key * key)
     558             : {
     559     2755749 :         if (!key) return -1;
     560             : 
     561     2755749 :         return keyGetMeta (key, "binary") != 0;
     562             : }
     563             : 
     564             : 
     565             : /**
     566             :  * Check if a key is string type.
     567             :  *
     568             :  * String values are null terminated and are not allowed to have any '\\0' characters
     569             :  * inside the string.
     570             :  *
     571             :  * Make sure to use this function and don't test the string type another way to
     572             :  * ensure compatibility and to write less error prone programs.
     573             :  *
     574             :  * @retval 1 if it is string
     575             :  * @retval 0 if it is not
     576             :  * @retval -1 on NULL pointer
     577             :  * @see keyGetString(), keySetString()
     578             :  * @param key the key to check
     579             :  * @ingroup keytest
     580             :  */
     581      252970 : int keyIsString (const Key * key)
     582             : {
     583      252970 :         if (!key) return -1;
     584             : 
     585      252970 :         return keyGetMeta (key, "binary") == 0;
     586             : }
     587             : 
     588             : 
     589             : /**
     590             :  * @internal
     591             :  *
     592             :  * Compare 2 keys.
     593             :  *
     594             :  * The returned flags bit array has 1s (differ) or 0s (equal) for each key
     595             :  * meta info compared, that can be logically ORed using @c #keyswitch_t flags.
     596             :  * @link keyswitch_t::KEY_NAME KEY_NAME @endlink,
     597             :  * @link keyswitch_t::KEY_VALUE KEY_VALUE @endlink,
     598             :  * @link keyswitch_t::KEY_OWNER KEY_OWNER @endlink,
     599             :  * @link keyswitch_t::KEY_COMMENT KEY_COMMENT @endlink,
     600             :  * @link keyswitch_t::KEY_META KEY_META @endlink (will be set in addition to owner and comment),
     601             :  *
     602             :  * @par A very simple example would be
     603             :  * @code
     604             :  Key *key1, *key;
     605             :  uint32_t changes;
     606             : 
     607             : // omited key1 and key2 initialization and manipulation
     608             : 
     609             : changes=keyCompare(key1,key2);
     610             : 
     611             : if (changes == 0) printf("key1 and key2 are identicall\n");
     612             : 
     613             : if (changes & KEY_VALUE)
     614             : printf("key1 and key2 have different values\n");
     615             : 
     616             : if (changes & KEY_UID)
     617             : printf("key1 and key2 have different UID\n");
     618             : 
     619             :  *
     620             :  * @endcode
     621             :  *
     622             :  *
     623             :  * @par Example of very powerful specific Key lookup in a KeySet:
     624             :  * @code
     625             :  Key *base = keyNew ("/sw/MyApp/something", KEY_END);
     626             :  KDB *handle = kdbOpen(base);
     627             :  KeySet *ks=ksNew(0, KS_END);
     628             :  Key *current;
     629             :  uint32_t match;
     630             :  uint32_t interests;
     631             : 
     632             : 
     633             :  kdbGet(handle, ks, base);
     634             : 
     635             : // we are interested only in key type and access permissions
     636             : interests=(KEY_TYPE | KEY_MODE);
     637             : 
     638             : ksRewind(ks);   // put cursor in the beginning
     639             : while ((curren=ksNext(ks))) {
     640             : match=keyCompare(current,base);
     641             : 
     642             : if ((~match & interests) == interests)
     643             : printf("Key %s has same type and permissions of base key",keyName(current));
     644             : 
     645             : // continue walking in the KeySet....
     646             : }
     647             : 
     648             : // now we want same name and/or value
     649             : interests=(KEY_NAME | KEY_VALUE);
     650             : 
     651             : // we don't really need ksRewind(), since previous loop achieved end of KeySet
     652             : ksRewind(ks);
     653             : while ((current=ksNext(ks))) {
     654             : match=keyCompare(current,base);
     655             : 
     656             : if ((~match & interests) == interests) {
     657             : printf("Key %s has same name, value, and sync status
     658             : of base key",keyName(current));
     659             : }
     660             : // continue walking in the KeySet....
     661             : }
     662             : 
     663             : ksDel(ks);
     664             : kdbClose (handle, base);
     665             : keyDel(base);
     666             : * @endcode
     667             : *
     668             : * @return a bit array pointing the differences
     669             : * @param key1 first key
     670             : * @param key2 second key
     671             : * @see #keyswitch_t
     672             : * @ingroup keytest
     673             :         */
     674          86 : keyswitch_t keyCompare (const Key * key1, const Key * key2)
     675             : {
     676          86 :         if (!key1 && !key2) return 0;
     677          86 :         if (!key1 || !key2) return KEY_NULL;
     678             : 
     679          66 :         keyswitch_t ret = 0;
     680          66 :         ssize_t nsize1 = keyGetNameSize (key1);
     681          66 :         ssize_t nsize2 = keyGetNameSize (key2);
     682          66 :         const char * name1 = keyName (key1);
     683          66 :         const char * name2 = keyName (key2);
     684          66 :         const Key * comment1 = keyGetMeta (key1, "comment");
     685          66 :         const Key * comment2 = keyGetMeta (key2, "comment");
     686          66 :         const char * owner1 = keyOwner (key1);
     687          66 :         const char * owner2 = keyOwner (key2);
     688          66 :         const void * value1 = keyValue (key1);
     689          66 :         const void * value2 = keyValue (key2);
     690          66 :         ssize_t size1 = keyGetValueSize (key1);
     691          66 :         ssize_t size2 = keyGetValueSize (key2);
     692             : 
     693             :         // TODO: might be (binary) by chance
     694          66 :         if (strcmp (keyString (comment1), keyString (comment2))) ret |= KEY_COMMENT;
     695             : 
     696          66 :         if (strcmp (owner1, owner2)) ret |= KEY_OWNER;
     697             : 
     698          66 :         if (keyCompareMeta (key1, key2)) ret |= KEY_META;
     699             : 
     700          66 :         if (nsize1 != nsize2)
     701           2 :                 ret |= KEY_NAME;
     702          64 :         else if (!name1 || !name2)
     703           0 :                 ret |= KEY_NAME;
     704          64 :         else if (strcmp (name1, name2))
     705           0 :                 ret |= KEY_NAME;
     706             : 
     707             : 
     708          66 :         if (size1 != size2)
     709           6 :                 ret |= KEY_VALUE;
     710          60 :         else if (!value1 || !value2)
     711           0 :                 ret |= KEY_VALUE;
     712          60 :         else if (memcmp (value1, value2, size1))
     713           4 :                 ret |= KEY_VALUE;
     714             : 
     715             :         // TODO: rewind metadata to previous position
     716             :         return ret;
     717             : }
     718             : 
     719             : /**
     720             :  * @brief Compares metadata of two keys
     721             :  *
     722             :  * @retval KEY_META if there is a difference
     723             :  * @retval 0 if metadata is identical
     724             :  */
     725          66 : int keyCompareMeta (const Key * k1, const Key * k2)
     726             : {
     727             :         const Key * meta1;
     728             : 
     729          66 :         Key * key1 = (Key *) k1;
     730          66 :         Key * key2 = (Key *) k2;
     731             : 
     732          66 :         keyRewindMeta (key1);
     733          66 :         keyRewindMeta (key2);
     734         196 :         while ((meta1 = keyNextMeta (key1)) != 0)
     735             :         {
     736          98 :                 const Key * meta2 = keyNextMeta (key2);
     737          98 :                 if (!meta2)
     738             :                 {
     739             :                         return KEY_META;
     740             :                 }
     741             : 
     742          90 :                 if (strcmp (keyName (meta1), keyName (meta2))) return KEY_META;
     743          64 :                 if (strcmp (keyString (meta1), keyString (meta2))) return KEY_META;
     744             :         }
     745             : 
     746             :         // TODO: rewind metadata to previous position
     747             :         return 0;
     748             : }

Generated by: LCOV version 1.13