LCOV - code coverage report
Current view: top level - src/libs/merge - kdbmerge.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 170 212 80.2 %
Date: 2019-09-12 12:28:41 Functions: 18 18 100.0 %

          Line data    Source code
       1             : 
       2             : #include "kdbmerge.h"
       3             : #include "kdb.h"
       4             : #include "kdbassert.h"
       5             : #include "kdblogger.h"
       6             : #include "kdbprivate.h"
       7             : #include <ctype.h>
       8             : #include <stdbool.h>
       9             : #include <stdio.h>
      10             : #include <stdlib.h>
      11             : #include <string.h>
      12             : 
      13             : #define INT_BUF_SIZE 11 // Avoid math.h. int has at most 10 digits, +1 for \0
      14             : 
      15             : /**
      16             :  * @brief Get a statistical value from an information key
      17             :  * @param informationKey contains the statistics in its meta information
      18             :  * @param metaName which statistic to get
      19             :  * @param retval the statistical value
      20             :  * @param retval -1 on error
      21             :  */
      22         668 : static int getStatisticalValue (Key * informationKey, char * metaName)
      23             : {
      24         668 :         const Key * metaKey = keyGetMeta (informationKey, metaName);
      25         668 :         if (metaKey == NULL)
      26             :         {
      27             :                 return 0;
      28             :         }
      29         108 :         char * test = elektraMalloc (keyGetValueSize (metaKey));
      30         108 :         if (keyGetString (metaKey, test, keyGetValueSize (metaKey)) < 0)
      31             :         {
      32           0 :                 ELEKTRA_SET_INTERNAL_ERROR (informationKey, "Could not get statistical value.");
      33           0 :                 return -1;
      34             :         }
      35         108 :         int asInt = atoi (test);
      36         108 :         elektraFree (test);
      37         108 :         return asInt;
      38             : }
      39             : 
      40             : /**
      41             :  * @brief Set a statistical value in an information key.
      42             :  * @param informationKey contains the statistics in its meta information
      43             :  * @param metaName which statistic to set
      44             :  * @param value which value to set it to, must be a number
      45             :  *
      46             :  * This enforces that a number is set.
      47             :  */
      48         108 : static void setStatisticalValue (Key * informationKey, char * metaName, int value)
      49             : {
      50             :         char stringy[INT_BUF_SIZE];
      51         108 :         int printsize = snprintf (stringy, INT_BUF_SIZE, "%d", value);
      52         108 :         if (printsize < INT_BUF_SIZE)
      53             :         {
      54         108 :                 ELEKTRA_SET_INTERNAL_ERROR (informationKey, "Statistical value was too large for its buffer.");
      55             :         }
      56         108 :         ssize_t size = keySetMeta (informationKey, metaName, stringy);
      57         108 :         if (size <= 0)
      58             :         {
      59           0 :                 ELEKTRA_SET_INTERNAL_ERROR (informationKey, "Could not set statistical value.");
      60             :         }
      61         108 : }
      62             : 
      63             : /**
      64             :  * @brief Increase a statistical value in an information key by one
      65             :  * @param informationKey contains the statistics in its meta information
      66             :  * @param metaName which statistic to increase
      67             :  * @retval new value
      68             :  */
      69         108 : static int increaseStatisticalValue (Key * informationKey, char * metaName)
      70             : {
      71         108 :         int value = getStatisticalValue (informationKey, metaName);
      72         108 :         value++;
      73         108 :         setStatisticalValue (informationKey, metaName, value);
      74         108 :         return value;
      75             : }
      76             : 
      77             : /**
      78             :  * @param informationKey contains the statistics in its meta information
      79             :  * @retval the number of non-overlap conflicts where only base exists
      80             :  */
      81             : static int getNonOverlapOnlyBaseConflicts (Key * informationKey)
      82             : {
      83         112 :         return getStatisticalValue (informationKey, "nonOverlapOnlyBaseCounter");
      84             : }
      85             : 
      86             : /**
      87             :  * @param informationKey contains the statistics in its meta information
      88             :  * @retval the number of non-overlap conflicts where all keys existed
      89             :  */
      90         112 : static int getNonOverlapAllExistConflicts (Key * informationKey)
      91             : {
      92         112 :         int nonOverlapAllExistCounter = getStatisticalValue (informationKey, "nonOverlapAllExistCounter");
      93         112 :         if (nonOverlapAllExistCounter % 3 != 0)
      94             :         {
      95           0 :                 ELEKTRA_SET_INTERNAL_ERROR (informationKey, "Parameter input must not be null.");
      96             :         }
      97         112 :         return nonOverlapAllExistCounter / 3;
      98             : }
      99             : 
     100             : /**
     101             :  * @param informationKey contains the statistics in its meta information
     102             :  * @retval the number of non-overlap conflicts where the key in the base set was empty
     103             :  */
     104         112 : static int getNonOverlapBaseEmptyConflicts (Key * informationKey)
     105             : {
     106         112 :         int nonOverlapBaseEmptyCounter = getStatisticalValue (informationKey, "nonOverlapBaseEmptyCounter");
     107         112 :         if (nonOverlapBaseEmptyCounter % 2 != 0)
     108             :         {
     109           0 :                 ELEKTRA_SET_INTERNAL_ERROR (informationKey, "Parameter input must not be null.");
     110             :         }
     111         112 :         return nonOverlapBaseEmptyCounter / 2;
     112             : }
     113             : 
     114             : /**
     115             :  * @param informationKey contains the statistics in its meta information
     116             :  * @retval the number of overlaps where all 3 keys were different
     117             :  */
     118         112 : static int getOverlap3different (Key * informationKey)
     119             : {
     120         112 :         int overlap3different = getStatisticalValue (informationKey, "overlap3different");
     121         112 :         if (overlap3different % 3 != 0)
     122             :         {
     123           0 :                 ELEKTRA_SET_INTERNAL_ERROR (informationKey, "Parameter input must not be null.");
     124             :         }
     125         112 :         return overlap3different / 3;
     126             : }
     127             : 
     128             : /**
     129             :  * @param informationKey contains the statistics in its meta information
     130             :  * @retval the number of overlaps where one key was empty, thus the other two keys had different values
     131             :  */
     132         112 : static int getOverlap1empty (Key * informationKey)
     133             : {
     134         112 :         int overlap1empty = getStatisticalValue (informationKey, "overlap1empty");
     135         112 :         if (overlap1empty % 2 != 0)
     136             :         {
     137           0 :                 ELEKTRA_SET_INTERNAL_ERROR (informationKey, "Parameter input must not be null.");
     138             :         }
     139         112 :         return overlap1empty / 2;
     140             : }
     141             : 
     142             : /**
     143             :  * @param informationKey contains the statistics in its meta information
     144             :  * @retval the number of overlaps that happened
     145             :  */
     146             : static int getTotalOverlaps (Key * informationKey)
     147             : {
     148         112 :         return getOverlap1empty (informationKey) + getOverlap3different (informationKey);
     149             : }
     150             : 
     151             : /**
     152             :  * @param informationKey contains the statistics in its meta information
     153             :  * @retval the number of non-overlaps that happened
     154             :  */
     155         112 : static int getTotalNonOverlaps (Key * informationKey)
     156             : {
     157         224 :         return getNonOverlapBaseEmptyConflicts (informationKey) + getNonOverlapAllExistConflicts (informationKey) +
     158         112 :                getNonOverlapOnlyBaseConflicts (informationKey);
     159             : }
     160             : 
     161             : /**
     162             :  * @param informationKey contains the statistics in its meta information
     163             :  * @retval the number of overlaps and non-overlaps that happened
     164             :  */
     165         112 : static int getTotalConflicts (Key * informationKey)
     166             : {
     167         224 :         return getTotalNonOverlaps (informationKey) + getTotalOverlaps (informationKey);
     168             : }
     169             : 
     170             : /**
     171             :  * @brief Removes one string from the other
     172             :  * @param sub this will be removed from string
     173             :  * @param string sub is removed from this char *
     174             :  * @returns the resulting string
     175             :  */
     176         246 : static char * strremove (char * string, const char * sub)
     177             : {
     178         246 :         size_t length = strlen (sub);
     179         246 :         if (length > 0)
     180             :         {
     181             :                 char * p = string;
     182         492 :                 while ((p = strstr (p, sub)) != NULL)
     183             :                 {
     184         246 :                         memmove (p, p + length, strlen (p + length) + 1);
     185             :                 }
     186             :         }
     187         246 :         return string;
     188             : }
     189             : 
     190             : /**
     191             :  *  @brief This is the counterpart to the removeRoot function
     192             :  *  @param input keys are from here
     193             :  *  @param result all keys with extended name will be appended here
     194             :  *  @param informationKey errors will be set here
     195             :  *  @retval -1 on error
     196             :  *  @retval  0 on success
     197             :  */
     198          96 : static int prependStringToAllKeyNames (KeySet * result, KeySet * input, const char * string, Key * informationKey)
     199             : {
     200          96 :         if (input == NULL)
     201             :         {
     202           0 :                 ELEKTRA_SET_INTERNAL_ERROR (informationKey, "Parameter input must not be null.");
     203           0 :                 return -1;
     204             :         }
     205          96 :         if (result == NULL)
     206             :         {
     207           0 :                 ELEKTRA_SET_INTERNAL_ERROR (informationKey, "Parameter result must not be null.");
     208           0 :                 return -1;
     209             :         }
     210          96 :         if (string == NULL)
     211             :         {
     212           0 :                 ELEKTRA_SET_INTERNAL_ERROR (informationKey, "Parameter string must not be null.");
     213           0 :                 return -1;
     214             :         }
     215             :         Key * key;
     216          96 :         ksRewind (input);
     217         262 :         while ((key = ksNext (input)) != NULL)
     218             :         {
     219          70 :                 char * newName = elektraMalloc (keyGetNameSize (key) + strlen (string));
     220          70 :                 strcpy (newName, string);
     221          70 :                 strcat (newName, keyName (key));
     222          70 :                 Key * duplicateKey = keyDup (key); // keySetName returns -1 if key was inserted to a keyset before
     223          70 :                 int status = keySetName (duplicateKey, newName);
     224          70 :                 elektraFree (newName);
     225          70 :                 if (status < 0)
     226             :                 {
     227           0 :                         ELEKTRA_SET_INTERNAL_ERROR (informationKey, "Could not set key name.");
     228             :                 }
     229          70 :                 status = ksAppendKey (result, duplicateKey);
     230          70 :                 if (status < 0)
     231             :                 {
     232           0 :                         ELEKTRA_SET_INTERNAL_ERROR (informationKey, "Could not append key.");
     233             :                 }
     234             :         }
     235             :         return 0;
     236             : }
     237             : 
     238             : /**
     239             :  * @brief Remove the root from each key of a set
     240             :  * @param original the key set from which root will be rmeoved
     241             :  * @param root remove this from all the keys
     242             :  * @param informationKey will contain information if an error occurs
     243             :  * @returns a new key set without the root
     244             :  *
     245             :  * Example: If root is user/example and the KeySet contains a key with the name user/example/something then
     246             :  * the returned KeySet will contain the key /something.
     247             :  */
     248         336 : static KeySet * removeRoot (KeySet * original, Key * root, Key * informationKey)
     249             : {
     250         336 :         ksRewind (original);
     251         336 :         KeySet * result = ksNew (0, KS_END);
     252         336 :         const char * rootKeyNameString = keyName (root);
     253             :         Key * currentKey;
     254         918 :         while ((currentKey = ksNext (original)) != NULL)
     255             :         {
     256         246 :                 char * currentKeyNameString = elektraMalloc (keyGetNameSize (currentKey));
     257         246 :                 if (keyGetName (currentKey, currentKeyNameString, keyGetNameSize (currentKey)) < 0)
     258             :                 {
     259           0 :                         ELEKTRA_ASSERT (false, "ERROR: This should not happen");
     260             :                         elektraFree (currentKeyNameString);
     261             :                         ksDel (result);
     262             :                         return NULL;
     263             :                 };
     264         246 :                 if (keyIsBelow (root, currentKey) || keyCmp (currentKey, root) == 0)
     265         246 :                 {
     266         246 :                         Key * keyCopy = keyDup (currentKey);
     267             :                         int retVal;
     268         246 :                         if (keyIsBelow (root, currentKey))
     269             :                         {
     270         246 :                                 currentKeyNameString = strremove (currentKeyNameString, rootKeyNameString);
     271         246 :                                 retVal = keySetName (keyCopy, currentKeyNameString);
     272             :                         }
     273             :                         else
     274             :                         {
     275             :                                 // If the root itself is in the keyset then create a special name for it as it would be empty otherwise
     276           0 :                                 retVal = keySetName (keyCopy, "/root");
     277             :                         }
     278         246 :                         if (retVal < 0)
     279             :                         {
     280           0 :                                 elektraFree (currentKeyNameString);
     281           0 :                                 ksDel (result);
     282           0 :                                 ELEKTRA_SET_INTERNAL_ERROR (informationKey, "Setting new key name was not possible.");
     283             :                         }
     284         246 :                         ksAppendKey (result, keyCopy);
     285         246 :                         elektraFree (currentKeyNameString);
     286             :                 }
     287             :                 else
     288             :                 {
     289           0 :                         elektraFree (currentKeyNameString);
     290           0 :                         ksDel (result);
     291           0 :                         ELEKTRA_SET_INTERNAL_ERROR (informationKey, "Setting new key name was not possible.");
     292             :                 }
     293             :         }
     294             :         return result;
     295             : }
     296             : 
     297             : /**
     298             :  * @brief Compares two keys
     299             :  * @retval true if two keys are equal
     300             :  * @retval false otherwise
     301             :  */
     302         636 : static bool keysAreEqual (Key * a, Key * b)
     303             : {
     304             :         // Two nothings are not keys and thus false too
     305         636 :         if (a == NULL || b == NULL)
     306             :         {
     307             :                 return false;
     308             :         }
     309         600 :         if (keyGetValueSize (a) != keyGetValueSize (b))
     310             :         {
     311             :                 return false;
     312             :         }
     313         600 :         if (0 != memcmp (keyValue (a), keyValue (b), keyGetValueSize (a)))
     314             :         {
     315             :                 return false;
     316             :         }
     317         276 :         return true;
     318             : }
     319             : 
     320             : /**
     321             :  * @brief Helper function for checkSingleSet for when the key (name is relevant) is only in two of the three key sets
     322             :  * @retval -1 on error
     323             :  * @retval 0 on success
     324             :  */
     325          72 : static int twoOfThreeExistHelper (Key * checkedKey, Key * keyInFirst, Key * keyInSecond, KeySet * result, bool checkedIsDominant,
     326             :                                   int baseIndicator, Key * informationKey)
     327             : {
     328             :         Key * existingKey;
     329          72 :         bool thisConflict = false;
     330             :         /** This if or the else if happen when our and their set have a key that
     331             :          *  base does not have. This is a conflict case.
     332             :          *  This place is hit twice, thus overlap1empty gets double the amount of errors.
     333             :          */
     334          72 :         if (keyInFirst != NULL)
     335             :         {
     336             :                 existingKey = keyInFirst;
     337             :         }
     338          36 :         else if (keyInSecond != NULL)
     339             :         {
     340             :                 existingKey = keyInSecond;
     341             :         }
     342             :         else
     343             :         {
     344           0 :                 ELEKTRA_SET_INTERNAL_ERROR (informationKey, "Could not append key.");
     345           0 :                 return -1;
     346             :         }
     347             :         if (thisConflict)
     348             :         {
     349             :                 increaseStatisticalValue (informationKey, "nonOverlapBaseEmptyCounter");
     350             :         }
     351          72 :         if (!keysAreEqual (checkedKey, existingKey))
     352             :         {
     353             :                 // overlap  with single empty
     354             :                 // This spot is hit twice for a single overlap conflict. Thus calculate half later on.
     355          36 :                 increaseStatisticalValue (informationKey, "overlap1empty");
     356          36 :                 if (checkedIsDominant) // TODO This also happens when there is no conflict
     357             :                 {
     358           8 :                         if (ksAppendKey (result, checkedKey) < 0)
     359             :                         {
     360           0 :                                 ELEKTRA_SET_INTERNAL_ERROR (informationKey, "Could not append key.");
     361           0 :                                 return -1;
     362             :                         }
     363             :                 }
     364             :         }
     365             :         else
     366             :         {
     367             :                 // uses the NULL properties of keysAreEqual
     368          36 :                 if (keysAreEqual (checkedKey, keyInFirst) && baseIndicator == 2)
     369             :                 {
     370           6 :                         thisConflict = true;
     371             :                 }
     372          36 :                 if (keysAreEqual (checkedKey, keyInSecond) && baseIndicator == 1)
     373             :                 {
     374           6 :                         thisConflict = true;
     375             :                 }
     376          36 :                 if (thisConflict)
     377             :                 {
     378             :                         // base is empty and other and their have the same (non-empty) value
     379             :                         // this is a conflict
     380          12 :                         increaseStatisticalValue (informationKey, "nonOverlapBaseEmptyCounter");
     381          12 :                         if (checkedIsDominant)
     382             :                         {
     383           4 :                                 if (ksAppendKey (result, checkedKey) < 0)
     384             :                                 {
     385           0 :                                         ELEKTRA_SET_INTERNAL_ERROR (informationKey, "Could not append key.");
     386           0 :                                         return -1;
     387             :                                 }
     388             :                         }
     389             :                 }
     390             :         }
     391             :         return 0;
     392             : }
     393             : 
     394             : /**
     395             :  * @brief Helper function for allExistHelper checking if two of the three keys are equal and acting accordingly (side effects!)
     396             :  * @retval true if exactly two of the three keys have the same value
     397             :  * @retval false otherwise
     398             :  */
     399         126 : static bool twoOfThoseKeysAreEqual (Key * checkedKey, Key * keyInFirst, Key * keyInSecond, KeySet * result, bool checkedIsDominant,
     400             :                                     int baseIndicator, Key * informationKey)
     401             : {
     402             :         /**
     403             :          * One example for the next 3 ifs
     404             :          *
     405             :          * Cell contents are the values of the keys (/#0, /#1, ...)
     406             :          * in the different key sets
     407             :          *     base     our      their
     408             :          * /#0 one      previous previous
     409             :          * /#1 two      one      one
     410             :          * /#2 three    two      two
     411             :          * /#3 four     three    three
     412             :          * /#4 five     four     four
     413             :          * /#5          five     five
     414             :          * In the area from top down to line /#4 (inclusive) each cell triggers
     415             :          * the nonOverlapAllExistCounter. However, we must not count one conflict
     416             :          * multiple times, thus divide by 3 as there are three key sets (=columns).
     417             :          */
     418         126 :         if (keysAreEqual (keyInFirst, keyInSecond))
     419             :         {
     420          36 :                 if (baseIndicator == 0)
     421             :                 {
     422             :                         /** This is a non-overlap conflict
     423             :                          *  Base is currently checked and has value A, their and our have a different value B
     424             :                          */
     425          12 :                         increaseStatisticalValue (informationKey, "nonOverlapAllExistCounter");
     426          12 :                         if (checkedIsDominant)
     427             :                         {
     428             :                                 // If base is also dominant then append it's key
     429           0 :                                 if (ksAppendKey (result, checkedKey) < 0)
     430             :                                 {
     431           0 :                                         ELEKTRA_SET_INTERNAL_ERROR (informationKey, "Could not append key.");
     432             :                                 }
     433             :                         }
     434             :                 }
     435             :                 return true;
     436             :         }
     437          90 :         else if (keysAreEqual (checkedKey, keyInFirst))
     438             :         {
     439          36 :                 if (baseIndicator == 0)
     440             :                 {
     441          12 :                         if (ksAppendKey (result, keyInSecond) < 0)
     442             :                         {
     443           0 :                                 ELEKTRA_SET_INTERNAL_ERROR (informationKey, "Could not append key.");
     444             :                         }
     445             :                 }
     446             :                 else
     447             :                 {
     448          24 :                         if (baseIndicator == 2)
     449             :                         {
     450             :                                 /** This is a non-overlap conflict
     451             :                                  *  Base is currently secondCompare and has value A, their and our have a different
     452             :                                  *  value B
     453             :                                  */
     454          12 :                                 increaseStatisticalValue (informationKey, "nonOverlapAllExistCounter");
     455          12 :                                 if (checkedIsDominant)
     456             :                                 {
     457             :                                         // If base is also dominant then append it's key
     458           4 :                                         if (ksAppendKey (result, checkedKey) < 0)
     459             :                                         {
     460           0 :                                                 ELEKTRA_SET_INTERNAL_ERROR (informationKey, "Could not append key.");
     461             :                                         }
     462             :                                 }
     463             :                         }
     464             :                 }
     465             :                 return true;
     466             :         }
     467          54 :         else if (keysAreEqual (checkedKey, keyInSecond))
     468             :         {
     469             : 
     470          36 :                 if (baseIndicator == 0)
     471             :                 {
     472          12 :                         if (ksAppendKey (result, keyInFirst) < 0)
     473             :                         {
     474           0 :                                 ELEKTRA_SET_INTERNAL_ERROR (informationKey, "Could not append key.");
     475             :                         }
     476             :                 }
     477             :                 else
     478             :                 {
     479          24 :                         if (baseIndicator == 1)
     480             :                         {
     481             :                                 /** This is a non-overlap conflict
     482             :                                  *  Base is currently firstCompare and has value A, their and our have a different
     483             :                                  *  value B
     484             :                                  */
     485          12 :                                 increaseStatisticalValue (informationKey, "nonOverlapAllExistCounter");
     486          12 :                                 if (checkedIsDominant)
     487             :                                 {
     488             :                                         // If base is also dominant then append it's key
     489           4 :                                         if (ksAppendKey (result, checkedKey) < 0)
     490             :                                         {
     491           0 :                                                 ELEKTRA_SET_INTERNAL_ERROR (informationKey, "Could not append key.");
     492             :                                         }
     493             :                                 }
     494             :                         }
     495             :                 }
     496             :                 return true;
     497             :         }
     498             :         else
     499             :         {
     500             :                 return false;
     501             :         }
     502             : }
     503             : 
     504             : /**
     505             :  * @brief Helper function for checkSingleSet for when a key exists in all key sets.
     506             :  * @retval -1 on error
     507             :  * @retval 0 on success
     508             :  */
     509         156 : static int allExistHelper (Key * checkedKey, Key * keyInFirst, Key * keyInSecond, KeySet * result, bool checkedIsDominant,
     510             :                            int baseIndicator, Key * informationKey)
     511             : {
     512         156 :         if (keysAreEqual (checkedKey, keyInFirst) && keysAreEqual (checkedKey, keyInSecond))
     513             :         {
     514             :                 /**
     515             :                  * append any of the three keys
     516             :                  * will be appended multiple times, but that doesn't matter for the result
     517             :                  */
     518          30 :                 if (ksAppendKey (result, checkedKey) < 0)
     519             :                 {
     520           0 :                         ELEKTRA_SET_INTERNAL_ERROR (informationKey, "Could not append key.");
     521           0 :                         return -1;
     522             :                 }
     523             :         }
     524             :         else
     525             :         {
     526         126 :                 if (!twoOfThoseKeysAreEqual (checkedKey, keyInFirst, keyInSecond, result, checkedIsDominant, baseIndicator, informationKey))
     527             :                 {
     528             :                         /**
     529             :                          * Overlap conflict case
     530             :                          *
     531             :                          * The same overlap conflict gets detected three times, once for each of the three invocations of
     532             :                          * checkSingleSet. However, only one of those three times is required. Thus use a getter function
     533             :                          * that calculates a third.
     534             :                          */
     535          18 :                         increaseStatisticalValue (informationKey, "overlap3different");
     536          18 :                         if (checkedIsDominant)
     537             :                         {
     538           4 :                                 if (ksAppendKey (result, checkedKey) < 0)
     539             :                                 {
     540           0 :                                         ELEKTRA_SET_INTERNAL_ERROR (informationKey, "Could not append key.");
     541           0 :                                         return -1;
     542             :                                 }
     543             :                         }
     544             :                 }
     545             :         }
     546             :         return 0;
     547             : }
     548             : 
     549             : 
     550             : /**
     551             :  * and the element is not already in the result key set.
     552             :  * It should be called 3 times, each time with a different of our key set, their key set and base key set as checkedSet parameter.
     553             :  * Which of the remaining two key sets is firstCompared or secondCompared is irrelevant.
     554             :  *
     555             :  * @param checkedIsDominant parameter is for the merge strategy. If a conflict occurs and checkedIsDominant is true then the current element
     556             :  * of checkedSet is inserted. Consequently, it has to be set to true for exactly one of the three calls of this function.
     557             :  *
     558             :  * @param baseIndicator indicates which of the three key sets is the base key set. 0 is checkedSet, 1 firstcompared, 2 secondCompared
     559             :  * @param informationKey will contain information if an error ocurred
     560             :  *
     561             :  * @retval -1 on error
     562             :  * @retval 0 on success
     563             :  *
     564             :  */
     565         336 : static int checkSingleSet (KeySet * checkedSet, KeySet * firstCompared, KeySet * secondCompared, KeySet * result, bool checkedIsDominant,
     566             :                            int baseIndicator, Key * informationKey)
     567             : {
     568         336 :         ksRewind (checkedSet);
     569         336 :         ksRewind (firstCompared);
     570         336 :         ksRewind (secondCompared);
     571             :         Key * checkedKey;
     572         918 :         while ((checkedKey = ksNext (checkedSet)) != NULL)
     573             :         {
     574             :                 /**
     575             :                  * Check if a key with the same name exists
     576             :                  * Nothing about values is said yet
     577             :                  */
     578         246 :                 Key * keyInFirst = ksLookup (firstCompared, checkedKey, 0);
     579         246 :                 Key * keyInSecond = ksLookup (secondCompared, checkedKey, 0);
     580         246 :                 if (keyInFirst != NULL && keyInSecond != NULL)
     581             :                 {
     582         156 :                         allExistHelper (checkedKey, keyInFirst, keyInSecond, result, checkedIsDominant, baseIndicator, informationKey);
     583             :                 }
     584          90 :                 else if (keyInFirst == NULL && keyInSecond == NULL)
     585             :                 {
     586          18 :                         if (baseIndicator == 0)
     587             :                         {
     588             :                                 /**
     589             :                                  * Non-overlap conflict https://www.gnu.org/software/diffutils/manual/html_node/diff3-Merging.html
     590             :                                  *
     591             :                                  * Here keys from base could be appended. But doing so is not useful.
     592             :                                  */
     593           6 :                                 increaseStatisticalValue (informationKey, "nonOverlapOnlyBaseCounter");
     594             :                         }
     595             :                         else
     596             :                         {
     597          12 :                                 if (ksAppendKey (result, checkedKey) < 0)
     598             :                                 {
     599           0 :                                         ELEKTRA_SET_INTERNAL_ERROR (informationKey, "Could not append key.");
     600             :                                 }
     601             :                         }
     602             :                 }
     603             :                 else
     604             :                 {
     605          72 :                         twoOfThreeExistHelper (checkedKey, keyInFirst, keyInSecond, result, checkedIsDominant, baseIndicator,
     606             :                                                informationKey);
     607             :                 }
     608             :         }
     609         336 :         return 0;
     610             : }
     611             : 
     612             : 
     613             : /**
     614             :  *
     615             :  * This function can incorporate changes from two modified versions (our and their) into a common preceding version (base) of a key set.
     616             :  * This lets you merge the sets of changes represented by the two newer key sets. This is called a three-way merge between key sets.
     617             :  *
     618             :  * @brief Join three key sets together
     619             :  * @param our our key set
     620             :  * @param ourRoot key that has the root of our as name
     621             :  * @param their their key set
     622             :  * @param theirRoot key that has the root of their as name
     623             :  * @param base base key set
     624             :  * @param baseRoot key that has the root of base as name
     625             :  * @param resultRoot the name of this key determines where the resulting key set will be stored
     626             :  * @param strategy specify which merge strategy to choose in case of a conflict
     627             :  * @param informationKey stores errors as well as statistics
     628             :  * @returns the merged key set and NULL on error
     629             :  */
     630         112 : KeySet * elektraMerge (KeySet * our, Key * ourRoot, KeySet * their, Key * theirRoot, KeySet * base, Key * baseRoot, Key * resultRoot,
     631             :                        int strategy, Key * informationKey)
     632             : {
     633             :         ELEKTRA_LOG ("cmerge starts with strategy %d", strategy);
     634         112 :         KeySet * result = ksNew (0, KS_END);
     635         112 :         KeySet * ourCropped = removeRoot (our, ourRoot, informationKey);
     636         112 :         KeySet * theirCropped = removeRoot (their, theirRoot, informationKey);
     637         112 :         KeySet * baseCropped = removeRoot (base, baseRoot, informationKey);
     638         112 :         ksRewind (ourCropped);
     639         112 :         ksRewind (theirCropped);
     640         112 :         ksRewind (baseCropped);
     641         112 :         bool ourDominant = false;
     642         112 :         bool theirDominant = false;
     643         112 :         switch (strategy)
     644             :         {
     645             :         case MERGE_STRATEGY_OUR:
     646          36 :                 ourDominant = true;
     647          36 :                 break;
     648             :         case MERGE_STRATEGY_THEIR:
     649          36 :                 theirDominant = true;
     650          36 :                 break;
     651             :         }
     652             : 
     653         112 :         checkSingleSet (baseCropped, ourCropped, theirCropped, result, false, 0, informationKey); // base is never dominant
     654         112 :         checkSingleSet (theirCropped, baseCropped, ourCropped, result, theirDominant, 1, informationKey);
     655         112 :         checkSingleSet (ourCropped, theirCropped, baseCropped, result, ourDominant, 2, informationKey);
     656         112 :         if (ksDel (ourCropped) != 0 || ksDel (theirCropped) != 0 || ksDel (baseCropped) != 0)
     657             :         {
     658           0 :                 ELEKTRA_SET_INTERNAL_ERROR (informationKey, "Could not delete a key set.");
     659           0 :                 return NULL;
     660             :         }
     661         112 :         if (getTotalConflicts (informationKey) > 0)
     662             :         {
     663          48 :                 if (strategy == MERGE_STRATEGY_ABORT)
     664             :                 {
     665          16 :                         ksDel (result);
     666          16 :                         ELEKTRA_SET_INTERNAL_ERROR (informationKey, "Abort strategy was set and at least one conflict occured.");
     667          16 :                         return NULL;
     668             :                 }
     669             :         }
     670          96 :         KeySet * resultWithRoot = ksNew (0, KS_END);
     671          96 :         prependStringToAllKeyNames (resultWithRoot, result, keyName (resultRoot), informationKey);
     672          96 :         ksDel (result);
     673          96 :         return resultWithRoot;
     674             : }

Generated by: LCOV version 1.13