LCOV - code coverage report
Current view: top level - src/plugins/ini - ini.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 798 893 89.4 %
Date: 2019-09-12 12:28:41 Functions: 36 37 97.3 %

          Line data    Source code
       1             : /**
       2             :  * @file
       3             :  *
       4             :  * @brief A plugin for reading and writing ini files
       5             :  *
       6             :  * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
       7             :  *
       8             :  */
       9             : 
      10             : #ifndef HAVE_KDBCONFIG
      11             : #include "kdbconfig.h"
      12             : #endif
      13             : 
      14             : #include "ini.h"
      15             : #include <ctype.h>
      16             : #include <errno.h>
      17             : #include <inih.h>
      18             : #include <kdbease.h>
      19             : #include <kdberrors.h>
      20             : #include <kdbhelper.h>
      21             : #include <kdbmeta.h>
      22             : #include <kdbos.h>
      23             : #include <kdbprivate.h>  //elektraReadArrayNumber
      24             : #include <kdbproposal.h> //elektraKsToMemArray
      25             : #include <stdlib.h>
      26             : #include <string.h>
      27             : 
      28             : 
      29             : char * keyNameGetOneLevel (const char *, size_t *);
      30             : 
      31             : int elektraIniOpen (Plugin * handle, Key * parentKey);
      32             : int elektraIniClose (Plugin * handle, Key * parentKey);
      33             : static char * findParent (Key *, Key *, KeySet *);
      34             : #include "contract.h"
      35             : static int iniCmpOrder (const void * a, const void * b);
      36             : 
      37             : #define INTERNAL_ROOT_SECTION "GLOBALROOT"
      38             : #define DEFAULT_DELIMITER '='
      39             : #define DEFAULT_COMMENT_CHAR '#'
      40             : 
      41             : char const * const ININAME_DELIM_SPECIAL_MASK = "\"%s\"%c";
      42             : char const * const ININAME_DELIM_MASK = "%s%c";
      43             : 
      44             : typedef enum
      45             : {
      46             :         NONE,
      47             :         BINARY,
      48             :         ALWAYS
      49             : } SectionHandling;
      50             : 
      51             : typedef struct
      52             : {
      53             :         short supportMultiline; /* defines whether multiline keys are supported */
      54             :         short preserverOrder;
      55             :         SectionHandling sectionHandling;
      56             :         short array;
      57             :         short mergeSections;
      58             :         short BOM;
      59             :         char * continuationString;
      60             :         char delim;
      61             :         char * lastOrder;
      62             :         char commentChar;
      63             :         KeySet * oldKS;
      64             :         Key * lastComments;
      65             : } IniPluginConfig;
      66             : 
      67             : typedef struct
      68             : {
      69             :         Key * parentKey;        /* the parent key of the result KeySet */
      70             :         KeySet * result;        /* the result KeySet */
      71             :         Key * collectedComment; /* buffer for collecting comments until a non comment key is reached */
      72             :         short array;
      73             :         short mergeSections;
      74             :         IniPluginConfig * pluginConfig;
      75             : } CallbackHandle;
      76             : 
      77             : 
      78      150919 : static void flushCollectedComment (CallbackHandle * handle, Key * key)
      79             : {
      80      150919 :         if (handle->collectedComment)
      81             :         {
      82        1091 :                 KeySet * comments = elektraMetaArrayToKS (handle->collectedComment, "comments");
      83             :                 Key * cur;
      84        1127 :                 while ((cur = ksNext (comments)) != NULL)
      85             :                 {
      86          36 :                         keySetMeta (key, keyName (cur), keyString (cur));
      87             :                 }
      88        1091 :                 ksDel (comments);
      89        1091 :                 keyRewindMeta (handle->collectedComment);
      90        3912 :                 while ((cur = (Key *) keyNextMeta (handle->collectedComment)) != NULL)
      91             :                 {
      92        2821 :                         if (!strncmp (keyName (cur), "meta/", 5)) keySetMeta (key, keyName (cur) + 5, keyString (cur));
      93             :                 }
      94        1091 :                 keyDel (handle->collectedComment);
      95        1091 :                 handle->collectedComment = NULL;
      96             :         }
      97      150919 : }
      98             : 
      99             : // TODO defined privately in internal.c, API break possible.
     100             : // Might consider moving this to the public API as it might be used by more plugins
     101             : size_t elektraUnescapeKeyName (const char * source, char * dest);
     102             : 
     103             : // TODO: this is very similar to elektraKeyAppendMetaLine in keytometa
     104       10827 : static int elektraKeyAppendLine (Key * target, const char * line)
     105             : {
     106       10827 :         if (!target) return 0;
     107       10827 :         if (!line) return 0;
     108             : 
     109             : 
     110       10827 :         char * buffer = elektraMalloc (keyGetValueSize (target) + strlen (line) + 1);
     111       10827 :         if (!buffer) return 0;
     112             : 
     113       10827 :         keyGetString (target, buffer, keyGetValueSize (target));
     114       10827 :         strcat (buffer, "\n");
     115       10827 :         strncat (buffer, line, elektraStrLen (line));
     116             : 
     117       10827 :         keySetString (target, buffer);
     118       10827 :         elektraFree (buffer);
     119       10827 :         return keyGetValueSize (target);
     120             : }
     121             : 
     122             : 
     123      301103 : static void keyAddUnescapedBasePath (Key * key, const char * path)
     124             : {
     125      301103 :         size_t size = 0;
     126      301103 :         char * p = keyNameGetOneLevel (path + size, &size);
     127      301103 :         if (*p && path[0] == '/')
     128             :         {
     129           0 :                 keyAddBaseName (key, path);
     130           0 :                 return;
     131             :         }
     132      817539 :         while (*p)
     133             :         {
     134      516436 :                 char * buffer = elektraMalloc (size + 1);
     135      516436 :                 strncpy (buffer, p, size);
     136      516436 :                 buffer[size] = 0;
     137      516436 :                 int ret = keyAddName (key, buffer);
     138      516436 :                 if (ret == -1)
     139             :                 {
     140           0 :                         char * tmp = elektraMalloc (keyGetFullNameSize (key) + strlen (buffer) + 2);
     141           0 :                         keyGetFullName (key, tmp, keyGetFullNameSize (key));
     142           0 :                         strcat (tmp, "/");
     143           0 :                         strcat (tmp, buffer);
     144           0 :                         ssize_t rc = keySetName (key, tmp);
     145           0 :                         if (rc == -1 && tmp[strlen (tmp) - 1] == '\\')
     146             :                         {
     147           0 :                                 tmp[strlen (tmp) - 1] = '\0';
     148           0 :                                 keySetName (key, tmp);
     149             :                         }
     150           0 :                         elektraFree (tmp);
     151             :                 }
     152      516436 :                 elektraFree (buffer);
     153      516436 :                 p = keyNameGetOneLevel (p + size, &size);
     154             :         }
     155             : }
     156             : 
     157      301103 : static Key * createUnescapedKey (Key * key, const char * name)
     158             : {
     159      301103 :         char * dupName = elektraStrDup (name);
     160      301103 :         keyAddUnescapedBasePath (key, dupName);
     161      301103 :         free (dupName);
     162      301103 :         return key;
     163             : }
     164             : 
     165       22548 : static void setOrderNumber (Key * parentKey, Key * key)
     166             : {
     167       22548 :         kdb_long_long_t order = 1;
     168       22548 :         const Key * orderKey = keyGetMeta (parentKey, "internal/ini/order");
     169       22548 :         if (orderKey != NULL)
     170             :         {
     171       22548 :                 char * ptr = (char *) keyString (orderKey);
     172       22548 :                 ++ptr; // skip #
     173       45134 :                 while (*ptr == '_')
     174             :                 {
     175          38 :                         ++ptr;
     176             :                 }
     177       22548 :                 elektraReadArrayNumber (ptr, &order);
     178             :         }
     179       22548 :         ++order;
     180             :         char buffer[ELEKTRA_MAX_ARRAY_SIZE];
     181       22548 :         elektraWriteArrayNumber (buffer, order);
     182       22548 :         keySetMeta (key, "internal/ini/order", buffer);
     183       22548 :         keySetMeta (parentKey, "internal/ini/order", buffer);
     184       22548 : }
     185         727 : static void setSubOrderNumber (Key * key, const char * oldOrder)
     186             : {
     187         727 :         char * lastIndexPtr = NULL;
     188         727 :         char * newOrder = elektraMalloc (elektraStrLen (oldOrder) + ELEKTRA_MAX_ARRAY_SIZE);
     189         727 :         if ((lastIndexPtr = strrchr (oldOrder, '/')))
     190             :         {
     191         245 :                 kdb_long_long_t subIndex = 0;
     192         245 :                 char * ptr = lastIndexPtr;
     193         245 :                 ++ptr; // skip /
     194         245 :                 ++ptr; // skip #
     195         490 :                 while (*ptr == '_')
     196             :                 {
     197           0 :                         ++ptr;
     198             :                 }
     199         245 :                 elektraReadArrayNumber (ptr, &subIndex);
     200         245 :                 ++subIndex;
     201         245 :                 int len = (lastIndexPtr + 1) - oldOrder;
     202             :                 char buffer[ELEKTRA_MAX_ARRAY_SIZE];
     203         245 :                 elektraWriteArrayNumber (buffer, subIndex);
     204         245 :                 sprintf (newOrder, "%.*s%s", len, oldOrder, buffer);
     205             :         }
     206             :         else
     207             :         {
     208         482 :                 sprintf (newOrder, "%s/#1", oldOrder);
     209             :         }
     210         727 :         keySetMeta (key, "internal/ini/order", newOrder);
     211         727 :         elektraFree (newOrder);
     212         727 : }
     213             : 
     214             : 
     215      164610 : static void iniBomHandler (void * vhandle, short BOM)
     216             : {
     217      164610 :         CallbackHandle * handle = (CallbackHandle *) vhandle;
     218      164610 :         IniPluginConfig * pluginConfig = (IniPluginConfig *) handle->pluginConfig;
     219      164610 :         if (BOM)
     220             :         {
     221           0 :                 pluginConfig->BOM = 1;
     222             :         }
     223             :         else
     224             :         {
     225      164610 :                 pluginConfig->BOM = 0;
     226             :         }
     227      164610 : }
     228             : 
     229      136269 : static void setKeyOrderNumber (Key * sectionKey, Key * key)
     230             : {
     231      136269 :         const Key * childMeta = keyGetMeta (sectionKey, "internal/ini/key/last");
     232      136269 :         keySetMeta (key, "internal/ini/key/number", keyString (childMeta));
     233      136269 :         Key * newChild = keyDup (childMeta);
     234      136269 :         keyAddName (newChild, keyString (newChild));
     235      136269 :         elektraArrayIncName (newChild);
     236      136269 :         keySetMeta (sectionKey, "internal/ini/key/last", keyBaseName (newChild));
     237      136269 :         keyDel (newChild);
     238      136269 :         keySetMeta (key, "internal/ini/order", keyString (keyGetMeta (sectionKey, "internal/ini/order")));
     239      136269 : }
     240             : 
     241          15 : static int iniKeyToElektraArray (CallbackHandle * handle, Key * existingKey, Key * appendKey, const char * value)
     242             : {
     243             : 
     244          15 :         if (keyGetMeta (existingKey, "internal/ini/array"))
     245             :         {
     246             :                 // array already exists, appending new key
     247           8 :                 const char * lastIndex = keyString (keyGetMeta (existingKey, "internal/ini/array"));
     248           8 :                 keyAddBaseName (appendKey, lastIndex);
     249           8 :                 keySetMeta (appendKey, "internal/ini/array", 0);
     250           8 :                 keySetMeta (appendKey, "internal/ini/order", 0);
     251           8 :                 keySetMeta (appendKey, "internal/ini/parent", 0);
     252           8 :                 if (elektraArrayIncName (appendKey) == 1)
     253             :                 {
     254             :                         return -1;
     255             :                 }
     256           8 :                 keySetString (appendKey, value);
     257           8 :                 keySetMeta (appendKey, "internal/ini/arrayMember", "");
     258           8 :                 keySetMeta (appendKey, "internal/ini/order", keyString (keyGetMeta (existingKey, "internal/ini/order")));
     259           8 :                 ksAppendKey (handle->result, appendKey);
     260           8 :                 keySetMeta (existingKey, "internal/ini/array", keyBaseName (appendKey));
     261           8 :                 ksAppendKey (handle->result, existingKey);
     262             :         }
     263             :         else
     264             :         {
     265             :                 // creating a new array
     266           7 :                 Key * sectionKey = keyDup (appendKey);
     267           7 :                 keyAddName (sectionKey, "..");
     268           7 :                 char * origVal = elektraStrDup (keyString (existingKey));
     269           7 :                 keySetString (appendKey, "");
     270           7 :                 keySetMeta (appendKey, "internal/ini/array", "#1");
     271           7 :                 setOrderNumber (handle->parentKey, appendKey);
     272           7 :                 keySetMeta (appendKey, "internal/ini/parent", 0);
     273           7 :                 ksAppendKey (handle->result, keyDup (appendKey));
     274           7 :                 keySetMeta (appendKey, "internal/ini/arrayMember", "");
     275           7 :                 keySetMeta (appendKey, "internal/ini/array", 0);
     276           7 :                 keySetMeta (appendKey, "internal/ini/parent", 0);
     277           7 :                 keyAddName (appendKey, "#");
     278           7 :                 if (elektraArrayIncName (appendKey) == -1)
     279             :                 {
     280           0 :                         free (origVal);
     281             :                         return -1;
     282             :                 }
     283           7 :                 keySetString (appendKey, origVal);
     284           7 :                 ksAppendKey (handle->result, keyDup (appendKey));
     285           7 :                 free (origVal);
     286           7 :                 if (elektraArrayIncName (appendKey) == -1)
     287             :                 {
     288             :                         return -1;
     289             :                 }
     290           7 :                 keySetMeta (appendKey, "internal/ini/parent", 0);
     291           7 :                 keySetString (appendKey, value);
     292           7 :                 ksAppendKey (handle->result, keyDup (appendKey));
     293           7 :                 keyDel (appendKey);
     294           7 :                 keyDel (sectionKey);
     295             :         }
     296             :         return 1;
     297             : }
     298             : 
     299             : 
     300        8705 : static void insertKeyIntoKeySet (Key * parentKey, Key * key, KeySet * ks)
     301             : {
     302        8705 :         cursor_t savedCursor = ksGetCursor (ks);
     303        8705 :         char * parent = findParent (parentKey, key, ksDup (ks));
     304        8705 :         keySetMeta (key, "internal/ini/parent", parent);
     305        8705 :         if (keyGetMeta (key, "internal/ini/section"))
     306             :         {
     307         811 :                 keySetMeta (key, "internal/ini/key/last", "#0");
     308         811 :                 Key * cutKey = keyNew (parent, KEY_END);
     309         811 :                 KeySet * cutKS = ksCut (ks, cutKey);
     310             :                 Key * cur;
     311         811 :                 Key * prevKey = NULL;
     312        6035 :                 while ((cur = ksNext (cutKS)) != NULL)
     313             :                 {
     314        5223 :                         if (!keyGetMeta (cur, "internal/ini/section")) continue;
     315        1732 :                         if (strcmp (keyName (cur), keyName (key)) < 0)
     316             :                                 prevKey = cur;
     317             :                         else
     318             :                                 break;
     319             :                 }
     320         811 :                 if (prevKey)
     321             :                 {
     322         727 :                         const Key * orderMeta = keyGetMeta (prevKey, "internal/ini/order");
     323         727 :                         const char * oldOrder = keyString (orderMeta);
     324         727 :                         setSubOrderNumber (key, oldOrder);
     325             :                 }
     326             :                 else
     327             :                 {
     328          84 :                         setOrderNumber (parentKey, key);
     329             :                 }
     330         811 :                 ksAppend (ks, cutKS);
     331         811 :                 ksDel (cutKS);
     332         811 :                 keyDel (cutKey);
     333             :         }
     334             :         else
     335             :         {
     336        7894 :                 Key * sectionKey = ksLookupByName (ks, parent, KDB_O_NONE);
     337        7894 :                 if (!sectionKey)
     338             :                 {
     339          83 :                         keySetMeta (key, "internal/ini/order", "#0");
     340             :                 }
     341             :                 else
     342             :                 {
     343        7811 :                         setKeyOrderNumber (sectionKey, key);
     344             :                 }
     345             :         }
     346        8705 :         elektraFree (parent);
     347        8705 :         ksSetCursor (ks, savedCursor);
     348        8705 : }
     349             : 
     350      139304 : static int iniKeyToElektraKey (void * vhandle, const char * section, const char * name, const char * value, unsigned short lineContinuation)
     351             : {
     352      139304 :         CallbackHandle * handle = (CallbackHandle *) vhandle;
     353      139304 :         if ((!section || *section == '\0') && (!name || *name == '\0'))
     354             :         {
     355           0 :                 Key * rootKey = keyNew (keyName (handle->parentKey), KEY_END);
     356           0 :                 keySetString (rootKey, value);
     357           0 :                 flushCollectedComment (handle, rootKey);
     358           0 :                 ksAppendKey (handle->result, rootKey);
     359           0 :                 return 1;
     360             :         }
     361      139304 :         Key * appendKey = keyNew (keyName (handle->parentKey), KEY_END);
     362             :         Key * sectionKey;
     363      139304 :         if (!section || *section == '\0')
     364             :         {
     365         954 :                 section = INTERNAL_ROOT_SECTION;
     366             :         }
     367      139304 :         appendKey = createUnescapedKey (appendKey, section);
     368      139304 :         if (!strcmp (keyBaseName (appendKey), INTERNAL_ROOT_SECTION))
     369             :         {
     370         954 :                 sectionKey = ksLookup (handle->result, appendKey, KDB_O_NONE);
     371         954 :                 if (!sectionKey)
     372             :                 {
     373         247 :                         keySetMeta (appendKey, "internal/ini/order", "#0");
     374         247 :                         keySetMeta (appendKey, "internal/ini/key/last", "#0");
     375         247 :                         keySetMeta (appendKey, "internal/ini/section", "");
     376         247 :                         ksAppendKey (handle->result, keyDup (appendKey));
     377         247 :                         keySetMeta (appendKey, "internal/ini/order", "#0");
     378         247 :                         keySetMeta (appendKey, "internal/ini/key/last", "#0");
     379         247 :                         keySetMeta (appendKey, "internal/ini/section", 0);
     380         247 :                         sectionKey = ksLookup (handle->result, appendKey, KDB_O_NONE);
     381             :                 }
     382             :         }
     383             :         else
     384             :         {
     385      138350 :                 sectionKey = ksLookup (handle->result, appendKey, KDB_O_NONE);
     386             :         }
     387      139304 :         short mergeSections = 0;
     388      139304 :         Key * existingKey = NULL;
     389      139304 :         if ((existingKey = ksLookup (handle->result, appendKey, KDB_O_NONE)))
     390             :         {
     391      139304 :                 if (keyGetMeta (existingKey, "internal/ini/duplicate"))
     392             :                 {
     393           4 :                         mergeSections = 1;
     394             :                 }
     395             :         }
     396      139304 :         appendKey = createUnescapedKey (appendKey, name);
     397      139304 :         existingKey = ksLookup (handle->result, appendKey, KDB_O_NONE);
     398      139304 :         if (existingKey)
     399             :         {
     400             :                 // a key with the same name already exists
     401       10842 :                 if (handle->array)
     402             :                 {
     403             :                         // array support is turned on
     404          15 :                         return iniKeyToElektraArray (handle, existingKey, appendKey, value);
     405             :                 }
     406       10827 :                 else if (!lineContinuation)
     407             :                 {
     408           0 :                         keyDel (appendKey);
     409           0 :                         ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (handle->parentKey,
     410             :                                                                  "We found the key %s a second time in the INI file in section %s\n",
     411             :                                                                  keyName (existingKey), section);
     412           0 :                         return -1;
     413             :                 }
     414             :         }
     415             : 
     416      139289 :         if (value == NULL) keySetMeta (appendKey, "internal/ini/empty", "");
     417      139289 :         if (!lineContinuation)
     418             :         {
     419      128462 :                 flushCollectedComment (handle, appendKey);
     420      128462 :                 keySetString (appendKey, value);
     421      128462 :                 ksAppendKey (handle->result, appendKey);
     422      128462 :                 if (mergeSections)
     423             :                 {
     424           4 :                         keySetMeta (appendKey, "internal/ini/order", 0);
     425           4 :                         insertKeyIntoKeySet (handle->parentKey, appendKey, handle->result);
     426             :                 }
     427             :                 else
     428             :                 {
     429      128458 :                         setKeyOrderNumber (sectionKey, appendKey);
     430             :                 }
     431             :         }
     432             :         else
     433             :         {
     434       10827 :                 existingKey = ksLookup (handle->result, appendKey, KDB_O_NONE);
     435       10827 :                 keyDel (appendKey);
     436             :                 /* something went wrong before because this key should exist */
     437       10827 :                 if (!existingKey) return -1;
     438             : 
     439       10827 :                 elektraKeyAppendLine (existingKey, value);
     440             :         }
     441             : 
     442             : 
     443             :         return 1;
     444             : }
     445             : 
     446             : static short isSectionKey (Key * key)
     447             : {
     448      425400 :         return key && keyGetMeta (key, "internal/ini/section");
     449             : }
     450             : 
     451       22495 : static int iniSectionToElektraKey (void * vhandle, const char * section)
     452             : {
     453       22495 :         CallbackHandle * handle = (CallbackHandle *) vhandle;
     454       22495 :         Key * appendKey = keyNew (keyName (handle->parentKey), KEY_END);
     455       22495 :         createUnescapedKey (appendKey, section);
     456       22495 :         Key * existingKey = NULL;
     457       22495 :         if ((existingKey = ksLookup (handle->result, appendKey, KDB_O_NONE)))
     458             :         {
     459          38 :                 if (handle->mergeSections) keySetMeta (existingKey, "internal/ini/duplicate", "");
     460          38 :                 keyDel (appendKey);
     461          38 :                 return 1;
     462             :         }
     463       22457 :         setOrderNumber (handle->parentKey, appendKey);
     464       22457 :         keySetMeta (appendKey, "internal/ini/key/last", "#0");
     465       22457 :         keySetMeta (appendKey, "internal/ini/section", "");
     466       22457 :         flushCollectedComment (handle, appendKey);
     467       22457 :         ksAppendKey (handle->result, appendKey);
     468             : 
     469       22457 :         return 1;
     470             : }
     471             : 
     472        2809 : static int iniCommentToMeta (void * vhandle, const char * comment)
     473             : {
     474        2809 :         CallbackHandle * handle = (CallbackHandle *) vhandle;
     475        2809 :         if (!handle->collectedComment) handle->collectedComment = keyNew ("/comments", KEY_CASCADING_NAME, KEY_END);
     476        2809 :         if (strncmp (comment, "#@META ", 7))
     477          24 :                 elektraMetaArrayAdd (handle->collectedComment, "comments", comment);
     478             :         else
     479             :         {
     480             :                 // comment is an Elektra metakey
     481             : 
     482             :                 // strip "#@META " from comment
     483        2785 :                 char * localCopy = elektraStrDup (comment + 7);
     484        2785 :                 size_t len = strlen (localCopy);
     485        2785 :                 char * ptr = localCopy;
     486        2785 :                 char * name = ptr;
     487             :                 // skip keynames leading whitespace
     488        5570 :                 while (isspace (*name))
     489           0 :                         ++name;
     490             : 
     491             :                 // locate key/value delimiter "="
     492        2785 :                 ptr = strstr (localCopy, "=");
     493        2785 :                 if (ptr)
     494             :                 {
     495             : 
     496             :                         // skip keynames trailing whitespace starting left of delimiter
     497             :                         // and add nullbyte as string delimiter
     498        2785 :                         char * nameEnd = ptr - 1;
     499        8355 :                         while (isspace (*nameEnd))
     500        2785 :                                 --nameEnd;
     501        2785 :                         *(nameEnd + 1) = '\0';
     502        2785 :                         if (*ptr)
     503             :                         {
     504             : 
     505        2785 :                                 *ptr = '\0';
     506             :                                 // skip leading whitespace and drop trailing whitespace
     507        2785 :                                 char * value = ptr + 1;
     508        8355 :                                 while (isspace (*value))
     509        2785 :                                         ++value;
     510        2785 :                                 char * valueEnd = &localCopy[len - 1];
     511        5659 :                                 while (isspace (*valueEnd))
     512          89 :                                         --valueEnd;
     513        2785 :                                 if (*valueEnd) *(valueEnd + 1) = '\0';
     514        2785 :                                 char buffer[strlen (name) + sizeof ("meta/") + 1];
     515        2785 :                                 snprintf (buffer, sizeof (buffer), "meta/%s", name);
     516        2785 :                                 keySetMeta (handle->collectedComment, buffer, value);
     517             :                         }
     518             :                 }
     519        2785 :                 elektraFree (localCopy);
     520             :         }
     521        2809 :         return 1;
     522             : }
     523             : 
     524             : 
     525       45393 : int elektraIniOpen (Plugin * handle, Key * parentKey ELEKTRA_UNUSED)
     526             : {
     527       45393 :         KeySet * config = elektraPluginGetConfig (handle);
     528       45393 :         IniPluginConfig * pluginConfig = elektraMalloc (sizeof (IniPluginConfig));
     529       45393 :         pluginConfig->lastComments = NULL;
     530       45393 :         pluginConfig->lastOrder = NULL;
     531       45393 :         pluginConfig->BOM = 0;
     532       45393 :         Key * multilineKey = ksLookupByName (config, "/multiline", KDB_O_NONE);
     533       45393 :         Key * sectionHandlingKey = ksLookupByName (config, "/section", KDB_O_NONE);
     534       45393 :         Key * arrayKey = ksLookupByName (config, "/array", KDB_O_NONE);
     535       45393 :         Key * mergeSectionsKey = ksLookupByName (config, "/mergesections", KDB_O_NONE);
     536       45393 :         Key * contStringKey = ksLookupByName (config, "/linecont", KDB_O_NONE);
     537       45393 :         Key * delimiterKey = ksLookupByName (config, "/delimiter", KDB_O_NONE);
     538       45393 :         Key * commentKey = ksLookupByName (config, "/comment", KDB_O_NONE);
     539             : 
     540       45393 :         if (!commentKey)
     541             :         {
     542       45391 :                 pluginConfig->commentChar = DEFAULT_COMMENT_CHAR;
     543             :         }
     544             :         else
     545             :         {
     546           2 :                 pluginConfig->commentChar = keyString (commentKey)[0];
     547             :         }
     548             : 
     549             : 
     550       45393 :         if (!contStringKey)
     551             :         {
     552       45387 :                 pluginConfig->continuationString = elektraStrDup ("\t");
     553             :         }
     554             :         else
     555             :         {
     556           6 :                 pluginConfig->continuationString = elektraStrDup (keyString (contStringKey));
     557             :         }
     558       45393 :         pluginConfig->mergeSections = mergeSectionsKey != 0;
     559       45393 :         pluginConfig->array = arrayKey != 0;
     560       45393 :         if (!multilineKey)
     561             :         {
     562       45387 :                 pluginConfig->supportMultiline = 1;
     563             :         }
     564             :         else
     565             :         {
     566           6 :                 if (!strcmp (keyString (multilineKey), "0"))
     567             :                 {
     568           2 :                         pluginConfig->supportMultiline = 0;
     569             :                 }
     570             :                 else
     571             :                 {
     572           4 :                         pluginConfig->supportMultiline = 1;
     573             :                 }
     574             :         }
     575       45393 :         if (!sectionHandlingKey)
     576             :         {
     577       45367 :                 pluginConfig->sectionHandling = ALWAYS;
     578             :         }
     579             :         else
     580             :         {
     581          26 :                 if (!strcasecmp (keyString (sectionHandlingKey), "NONE"))
     582             :                 {
     583           0 :                         pluginConfig->sectionHandling = NONE;
     584             :                 }
     585          26 :                 else if (!strcasecmp (keyString (sectionHandlingKey), "NULL"))
     586             :                 {
     587          26 :                         pluginConfig->sectionHandling = BINARY;
     588             :                 }
     589           0 :                 else if (!strcasecmp (keyString (sectionHandlingKey), "ALWAYS"))
     590             :                 {
     591           0 :                         pluginConfig->sectionHandling = ALWAYS;
     592             :                 }
     593             :         }
     594       45393 :         if (delimiterKey)
     595             :         {
     596           0 :                 pluginConfig->delim = keyString (delimiterKey)[0];
     597             :         }
     598             :         else
     599             :         {
     600       45393 :                 pluginConfig->delim = DEFAULT_DELIMITER;
     601             :         }
     602       45393 :         pluginConfig->oldKS = NULL;
     603       45393 :         elektraPluginSetData (handle, pluginConfig);
     604             : 
     605       45393 :         return 0;
     606             : }
     607             : 
     608             : 
     609       45393 : int elektraIniClose (Plugin * handle, Key * parentKey ELEKTRA_UNUSED)
     610             : {
     611       45393 :         IniPluginConfig * pluginConfig = elektraPluginGetData (handle);
     612       45393 :         if (pluginConfig->oldKS) ksDel (pluginConfig->oldKS);
     613       45393 :         if (pluginConfig->lastOrder) elektraFree (pluginConfig->lastOrder);
     614       45393 :         if (pluginConfig->lastComments) keyDel (pluginConfig->lastComments);
     615       45393 :         elektraFree (pluginConfig->continuationString);
     616       45393 :         elektraFree (pluginConfig);
     617       45393 :         elektraPluginSetData (handle, 0);
     618       45393 :         return 0;
     619             : }
     620             : 
     621             : #if VERBOSE
     622             : static void outputDebug () __attribute__ ((unused));
     623             : 
     624             : static void outputDebug (KeySet * ks)
     625             : {
     626             :         Key * cur;
     627             :         ksRewind (ks);
     628             :         while ((cur = ksNext (ks)) != NULL)
     629             :         {
     630             :                 fprintf (stderr, "%s:(%.20s:_%.5x_)\t", keyName (cur), keyString (cur), *keyString (cur));
     631             :                 fprintf (stderr, " sync: %d", keyNeedSync (cur));
     632             :                 keyRewindMeta (cur);
     633             :                 const Key * meta;
     634             :                 while ((meta = keyNextMeta (cur)) != NULL)
     635             :                 {
     636             :                         fprintf (stderr, ", %s: %s", keyName (meta), (char *) keyValue (meta));
     637             :                 }
     638             :                 fprintf (stderr, "\n");
     639             :         }
     640             : }
     641             : #endif
     642             : 
     643      179476 : static char * findParent (Key * parentKey, Key * searchkey, KeySet * ks)
     644             : {
     645      179476 :         ksRewind (ks);
     646      179476 :         size_t offset = 0;
     647      179476 :         if (keyName (parentKey)[0] == '/' && keyName (searchkey)[0] != '/')
     648             :         {
     649           9 :                 const char * ptr = strchr (keyName (searchkey) + 1, '/');
     650           9 :                 if (ptr) offset = (ptr - keyName (searchkey)) + 1;
     651             :         }
     652      179476 :         Key * key = keyDup (searchkey);
     653             :         Key * lookedUp;
     654      760985 :         while (strcmp (keyName (key) + offset, keyName (parentKey)))
     655             :         {
     656      558781 :                 if (!strcmp (keyName (key), keyName (searchkey)))
     657             :                 {
     658      166837 :                         if (keyAddName (key, "..") <= 0)
     659             :                                 break;
     660             :                         else
     661      166837 :                                 continue;
     662             :                 }
     663      391944 :                 lookedUp = ksLookup (ks, key, KDB_O_NONE);
     664      391944 :                 if (lookedUp)
     665             :                 {
     666      390560 :                         if (isSectionKey (lookedUp)) break;
     667             :                 }
     668             : 
     669      235201 :                 if (keyAddName (key, "..") <= 0) break;
     670             :         }
     671      179476 :         lookedUp = ksLookup (ks, key, KDB_O_NONE);
     672      179476 :         if (!lookedUp) lookedUp = parentKey;
     673      179476 :         char * parentName = elektraStrDup (keyName (lookedUp));
     674      179476 :         keyDel (key);
     675      179476 :         ksDel (ks);
     676      179476 :         return parentName;
     677             : }
     678       18268 : static void setParents (KeySet * ks, Key * parentKey)
     679             : {
     680             :         Key * cur;
     681       18268 :         ksRewind (ks);
     682      206266 :         while ((cur = ksNext (ks)) != NULL)
     683             :         {
     684      169730 :                 char * parentName = findParent (parentKey, cur, ksDup (ks));
     685      169730 :                 if (parentName)
     686             :                 {
     687      169730 :                         keySetMeta (cur, "internal/ini/parent", parentName);
     688             :                 }
     689      169730 :                 elektraFree (parentName);
     690             :         }
     691       18268 : }
     692             : static void stripInternalData (Key * parentKey, KeySet *);
     693             : 
     694       18206 : static void incOrder (Key * key)
     695             : {
     696       18206 :         Key * orderKey = keyNew ("/", KEY_CASCADING_NAME, KEY_END);
     697       18206 :         keyAddName (orderKey, keyString (keyGetMeta (key, "internal/ini/order")));
     698       18206 :         elektraArrayIncName (orderKey);
     699       18206 :         keySetMeta (key, "internal/ini/order", keyBaseName (orderKey));
     700       18206 :         keyDel (orderKey);
     701       18206 : }
     702             : 
     703       17928 : int elektraIniGet (Plugin * handle, KeySet * returned, Key * parentKey)
     704             : {
     705             : 
     706             :         /* get all keys */
     707       17928 :         int errnosave = errno;
     708             : 
     709       17928 :         if (!strcmp (keyName (parentKey), "system/elektra/modules/ini"))
     710             :         {
     711         929 :                 KeySet * info = getPluginContract ();
     712             : 
     713         929 :                 ksAppend (returned, info);
     714         929 :                 ksDel (info);
     715         929 :                 return 1;
     716             :         }
     717             : 
     718             : 
     719       16999 :         FILE * fh = fopen (keyString (parentKey), "r");
     720       16999 :         if (!fh)
     721             :         {
     722           0 :                 ELEKTRA_SET_ERROR_GET (parentKey);
     723           0 :                 errno = errnosave;
     724           0 :                 return -1;
     725             :         }
     726             :         ELEKTRA_LOG_DEBUG ("Opened file %s for reading", keyString (parentKey));
     727             : 
     728       16999 :         KeySet * append = ksNew (0, KS_END);
     729             :         CallbackHandle cbHandle;
     730       16999 :         cbHandle.parentKey = parentKey;
     731       16999 :         cbHandle.result = append;
     732       16999 :         cbHandle.collectedComment = NULL;
     733             : 
     734             :         // ksAppendKey (cbHandle.result, keyDup(parentKey));
     735             : 
     736             :         struct IniConfig iniConfig;
     737       16999 :         iniConfig.keyHandler = iniKeyToElektraKey;
     738       16999 :         iniConfig.sectionHandler = iniSectionToElektraKey;
     739       16999 :         iniConfig.commentHandler = iniCommentToMeta;
     740       16999 :         iniConfig.bomHandler = iniBomHandler;
     741       16999 :         IniPluginConfig * pluginConfig = elektraPluginGetData (handle);
     742       16999 :         iniConfig.continuationString = pluginConfig->continuationString;
     743       16999 :         iniConfig.supportMultiline = pluginConfig->supportMultiline;
     744       16999 :         iniConfig.delim = pluginConfig->delim;
     745       16999 :         pluginConfig->BOM = 0;
     746       16999 :         if (pluginConfig->lastOrder && !keyGetMeta (parentKey, "internal/ini/order"))
     747           0 :                 keySetMeta (parentKey, "internal/ini/order", pluginConfig->lastOrder);
     748       16999 :         else if (!keyGetMeta (parentKey, "internal/ini/order"))
     749       14622 :                 keySetMeta (parentKey, "internal/ini/order", "#1");
     750       16999 :         cbHandle.array = pluginConfig->array;
     751       16999 :         cbHandle.mergeSections = pluginConfig->mergeSections;
     752       16999 :         cbHandle.pluginConfig = pluginConfig;
     753             :         ELEKTRA_LOG_DEBUG ("Try to parse file");
     754       16999 :         int ret = ini_parse_file (fh, &iniConfig, &cbHandle);
     755             :         ELEKTRA_LOG_DEBUG ("Parsed file");
     756       16999 :         if (cbHandle.collectedComment)
     757             :         {
     758           0 :                 pluginConfig->lastComments = keyDup (cbHandle.collectedComment);
     759           0 :                 keyDel (cbHandle.collectedComment);
     760             :         }
     761       16999 :         ksRewind (cbHandle.result);
     762       16999 :         stripInternalData (cbHandle.parentKey, cbHandle.result);
     763       16999 :         setParents (cbHandle.result, cbHandle.parentKey);
     764       16999 :         fclose (fh);
     765       16999 :         errno = errnosave;
     766       16999 :         if (ret == 0)
     767             :         {
     768       16997 :                 ksClear (returned);
     769       16997 :                 ksAppend (returned, cbHandle.result);
     770       16997 :                 if (pluginConfig->sectionHandling == ALWAYS)
     771             :                 {
     772       16993 :                         if (pluginConfig->oldKS) ksDel (pluginConfig->oldKS);
     773       16993 :                         pluginConfig->oldKS = ksDup (returned);
     774             :                 }
     775       16997 :                 elektraPluginSetData (handle, pluginConfig);
     776       16997 :                 ret = 1;
     777             :         }
     778             :         else
     779             :         {
     780           2 :                 switch (ret)
     781             :                 {
     782             :                 case -1:
     783           0 :                         ELEKTRA_SET_RESOURCE_ERROR (parentKey, "Unable to open the ini file");
     784           0 :                         break;
     785             :                 case -2:
     786           0 :                         ELEKTRA_SET_OUT_OF_MEMORY_ERROR (parentKey, "Memory allocation error while reading the ini file");
     787           0 :                         break;
     788             :                 default:
     789           2 :                         ELEKTRA_SET_VALIDATION_SYNTACTIC_ERRORF (parentKey, "Could not parse ini file %s. First error at line %d",
     790             :                                                                  keyString (parentKey), ret);
     791           2 :                         break;
     792             :                 }
     793             :                 ret = -1;
     794             :         }
     795       16999 :         ksDel (cbHandle.result);
     796       16999 :         if (pluginConfig->lastOrder) elektraFree (pluginConfig->lastOrder);
     797       16999 :         incOrder (parentKey);
     798       16999 :         pluginConfig->lastOrder = elektraStrDup (keyString (keyGetMeta (parentKey, "internal/ini/order")));
     799       16999 :         elektraPluginSetData (handle, pluginConfig);
     800       16999 :         return ret; /* success */
     801             : }
     802             : 
     803             : // TODO: # and ; comments get mixed up, patch inih to differentiate and
     804             : // create comment keys instead of writing metadata. Writing the meta
     805             : // data can be done by keytometa then
     806       17420 : void writeComments (Key * current, FILE * fh, const char commentChar)
     807             : {
     808       17420 :         const Key * meta = keyGetMeta (current, "comments");
     809       17420 :         if (meta == NULL) return;
     810          18 :         Key * lookupKey = keyDup (meta);
     811          18 :         keyAddBaseName (lookupKey, "#0");
     812          18 :         const Key * comment = keyGetMeta (current, keyName (lookupKey));
     813          72 :         while (comment != NULL)
     814             :         {
     815          36 :                 if (strlen (keyString (comment)) == 0)
     816             :                 {
     817          12 :                         fprintf (fh, "\n");
     818             :                 }
     819             :                 else
     820             :                 {
     821          24 :                         const char * ptr = keyString (comment);
     822          48 :                         while (*ptr)
     823             :                         {
     824          24 :                                 if (!isblank (*ptr))
     825             :                                 {
     826          24 :                                         if (*ptr == ';' || *ptr == '#')
     827          12 :                                                 fprintf (fh, "%s\n", keyString (comment));
     828             :                                         else
     829          12 :                                                 fprintf (fh, "%c%s\n", commentChar, keyString (comment));
     830             :                                         break;
     831             :                                 }
     832           0 :                                 ++ptr;
     833             :                         }
     834             :                 }
     835          36 :                 elektraArrayIncName (lookupKey);
     836          36 :                 comment = keyGetMeta (current, keyName (lookupKey));
     837             :         }
     838          18 :         keyDel (lookupKey);
     839             : }
     840             : static int keyContainsSpecialCharacter (const char *);
     841             : static int valueContainsSpecialCharacter (const char *);
     842             : 
     843           8 : void writeMultilineKey (Key * key, const char * iniName, FILE * fh, IniPluginConfig * config)
     844             : {
     845           8 :         size_t valueSize = keyGetValueSize (key);
     846           8 :         char * saveptr = 0;
     847           8 :         char * result = 0;
     848           8 :         char * value = elektraMalloc (valueSize);
     849           8 :         keyGetString (key, value, valueSize);
     850          16 :         result = strtok_r (value, "\n", &saveptr);
     851           8 :         if (keyContainsSpecialCharacter (iniName))
     852             :         {
     853           0 :                 fprintf (fh, ININAME_DELIM_SPECIAL_MASK, iniName, config->delim);
     854             :         }
     855             :         else
     856             :         {
     857           8 :                 fprintf (fh, ININAME_DELIM_MASK, iniName, config->delim);
     858             :         }
     859           8 :         if (result == NULL)
     860           0 :                 fprintf (fh, "\"\n%s\"", config->continuationString);
     861             :         else
     862             :         {
     863           8 :                 if (valueContainsSpecialCharacter (result))
     864           0 :                         fprintf (fh, "\"%s\"\n", result);
     865             :                 else
     866           8 :                         fprintf (fh, "%s\n", result);
     867             :         }
     868        2742 :         while ((result = strtok_r (0, "\n", &saveptr)) != 0)
     869             :         {
     870        1363 :                 if (valueContainsSpecialCharacter (result))
     871         681 :                         fprintf (fh, "%s\"%s\"\n", config->continuationString, result);
     872             :                 else
     873         682 :                         fprintf (fh, "%s%s\n", config->continuationString, result);
     874             :         }
     875             : 
     876           8 :         elektraFree (value);
     877           8 : }
     878             : 
     879             : 
     880             : /**
     881             :  * Returns the name of the corresponding ini key based on
     882             :  * the structure and parentKey of the supplied key.
     883             :  *
     884             :  * The returned string has to be freed by the caller
     885             :  *
     886             :  */
     887       17433 : static char * getIniName (Key * section, Key * key)
     888             : {
     889       17433 :         if (!strcmp (keyName (section), keyName (key))) return elektraStrDup (keyBaseName (key));
     890       17433 :         if (keyName (section)[0] == '/')
     891             :         {
     892           4 :                 if (!strcmp (keyName (section), strchr (keyName (key) + 1, '/'))) return elektraStrDup (keyBaseName (key));
     893             :         }
     894       17433 :         int slashCount = 0;
     895       17433 :         char * slashCounter = (char *) keyName (key);
     896     1107533 :         while (*slashCounter)
     897             :         {
     898     1072667 :                 if (*slashCounter == '/') ++slashCount;
     899     1072667 :                 ++slashCounter;
     900             :         }
     901       17433 :         int len = 0;
     902       17433 :         if (strcmp (keyName (section), "/")) len = strlen (keyName (section));
     903       17433 :         char * buffer = elektraCalloc ((strlen (keyName (key)) - len) + slashCount + 1);
     904       17433 :         char * ptr = NULL;
     905       17433 :         if (!strcmp (keyName (section), "/"))
     906             :         {
     907           0 :                 ptr = (char *) keyName (key);
     908             :         }
     909       17433 :         else if (keyName (section)[0] == '/' && keyName (key)[0] != '/')
     910           4 :         {
     911           4 :                 size_t offset = strchr (keyName (key) + 1, '/') - keyName (key);
     912           4 :                 ptr = (char *) keyName (key) + strlen (keyName (section)) + offset + 1;
     913             :         }
     914             :         else
     915             :         {
     916       17429 :                 ptr = (char *) keyName (key) + strlen (keyName (section)) + 1;
     917             :         }
     918             : 
     919       17433 :         size_t size = 0;
     920       17433 :         char * tmp = elektraStrDup (ptr);
     921       17433 :         char * p = keyNameGetOneLevel (tmp + size, &size);
     922       76954 :         while (*p)
     923             :         {
     924       42088 :                 char * name = elektraMalloc (size + 1);
     925       42088 :                 strncpy (name, p, size);
     926       42088 :                 name[size] = 0;
     927       42088 :                 strcat (buffer, name);
     928       42088 :                 strcat (buffer, "/");
     929       42088 :                 elektraFree (name);
     930       42088 :                 p = keyNameGetOneLevel (p + size, &size);
     931             :         }
     932       17433 :         free (tmp);
     933       17433 :         buffer[strlen (buffer) - 1] = '\0';
     934       17433 :         return buffer;
     935             : }
     936             : 
     937          88 : Key * getGlobalRoot (Key * parentKey, KeySet * ks)
     938             : {
     939          88 :         Key * lookup = keyNew (keyName (parentKey), KEY_END);
     940          88 :         keyAddName (lookup, INTERNAL_ROOT_SECTION);
     941          88 :         Key * found = ksLookup (ks, lookup, KDB_O_NONE);
     942          88 :         keyDel (lookup);
     943          88 :         return found;
     944             : }
     945             : 
     946          82 : int hasGlobalRoot (Key * parentKey, KeySet * ks)
     947             : {
     948          82 :         if (getGlobalRoot (parentKey, ks))
     949             :                 return 1;
     950             :         else
     951          76 :                 return 0;
     952             : }
     953             : 
     954          76 : void createGlobalRoot (Key * parentKey, KeySet * ks)
     955             : {
     956          76 :         Key * appendKey = keyNew (keyName (parentKey), KEY_END);
     957          76 :         keyAddName (appendKey, INTERNAL_ROOT_SECTION);
     958          76 :         keySetMeta (appendKey, "internal/ini/order", "#0");
     959          76 :         keySetMeta (appendKey, "internal/ini/key/last", "#0");
     960          76 :         ksAppendKey (ks, appendKey);
     961          76 : }
     962             : 
     963          11 : void arrayHandler (Key * parentKey, Key * newKey, Key * cur, Key * sectionKey, KeySet * newKS)
     964             : {
     965          11 :         Key * arrayParentLookup = keyDup (newKey);
     966          11 :         keySetBaseName (arrayParentLookup, 0);
     967          11 :         Key * arrayParent = ksLookup (newKS, arrayParentLookup, KDB_O_NONE);
     968          11 :         keyDel (arrayParentLookup);
     969          11 :         const Key * arrayMeta = keyGetMeta (arrayParent, "internal/ini/array");
     970          11 :         if (arrayMeta)
     971             :         {
     972           5 :                 if (strcmp (keyString (arrayMeta), keyBaseName (cur)) < 0)
     973             :                 {
     974           5 :                         keySetMeta (arrayParent, "internal/ini/array", keyBaseName (newKey));
     975           5 :                         keySetMeta (newKey, "internal/ini/arrayMember", "");
     976           5 :                         keySetMeta (newKey, "internal/ini/order", keyString (keyGetMeta (arrayParent, "internal/ini/order")));
     977           5 :                         keySetMeta (newKey, "internal/ini/key/number", 0); // keyBaseName (newKey));
     978           5 :                         ksAppendKey (newKS, newKey);
     979             :                 }
     980             :         }
     981           6 :         else if (arrayParent && !keyGetMeta (arrayParent, "internal/ini/section"))
     982           3 :         {
     983           3 :                 const char * oldVal = keyString (arrayParent);
     984           3 :                 keySetMeta (arrayParent, "internal/ini/array", keyBaseName (cur));
     985           3 :                 keySetMeta (arrayParent, "internal/ini/key/number", 0);
     986           3 :                 if (oldVal && strlen (oldVal))
     987             :                 {
     988           3 :                         Key * arrayInitKey = keyDup (arrayParent);
     989           3 :                         keyAddBaseName (arrayInitKey, "#0");
     990           3 :                         keySetString (arrayInitKey, oldVal);
     991           3 :                         keySetMeta (arrayInitKey, "internal/ini/array", 0);
     992           3 :                         keySetMeta (arrayInitKey, "internal/ini/arrayMember", "");
     993           3 :                         ksAppendKey (newKS, arrayInitKey);
     994           3 :                         keySetMeta (arrayInitKey, "internal/ini/order", keyString (keyGetMeta (arrayParent, "internal/ini/order")));
     995           3 :                         keySetMeta (arrayInitKey, "internal/ini/key/number", 0); // keyBaseName(arrayInitKey));
     996             :                 }
     997           3 :                 ksAppendKey (newKS, newKey);
     998           3 :                 keySetMeta (newKey, "internal/ini/order", keyString (keyGetMeta (arrayParent, "internal/ini/order")));
     999           3 :                 keySetMeta (newKey, "internal/ini/key/number", 0); // keyBaseName(newKey));
    1000           3 :                 keySetMeta (newKey, "internal/ini/arrayMember", "");
    1001             :         }
    1002           3 :         else if (arrayParent && (keyBaseName (newKey)[1] == '0'))
    1003           3 :         {
    1004           3 :                 Key * newParent = keyDup (arrayParent);
    1005           3 :                 keyAddName (newParent, "..");
    1006           3 :                 arrayParent = ksLookup (newKS, newParent, KDB_O_NONE);
    1007           3 :                 if (arrayParent)
    1008             :                 {
    1009           2 :                         keyDel (newParent);
    1010             :                 }
    1011             :                 else
    1012             :                 {
    1013           1 :                         arrayParent = newParent;
    1014           1 :                         keySetMeta (arrayParent, "internal/ini/section", "");
    1015           1 :                         keySetMeta (arrayParent, "internal/ini/array", keyBaseName (cur));
    1016           1 :                         ksAppendKey (newKS, arrayParent);
    1017           1 :                         insertKeyIntoKeySet (parentKey, arrayParent, newKS);
    1018           1 :                         keySetMeta (arrayParent, "internal/ini/key/last", 0);
    1019           1 :                         keySetMeta (arrayParent, "internal/ini/key/number", 0);
    1020             :                 }
    1021           3 :                 keySetMeta (newKey, "internal/ini/section", "");
    1022           3 :                 keySetMeta (arrayParent, "internal/ini/array", keyBaseName (cur));
    1023           3 :                 keyAddName (newKey, "..");
    1024           3 :                 ksAppendKey (newKS, newKey);
    1025           3 :                 insertKeyIntoKeySet (parentKey, newKey, newKS);
    1026           3 :                 keySetMeta (newKey, "internal/ini/section", 0);
    1027           3 :                 keySetString (newKey, keyString (cur));
    1028           3 :                 keySetMeta (newKey, "internal/ini/key/last", 0);
    1029           3 :                 keySetMeta (newKey, "internal/ini/key/number", 0);
    1030             :         }
    1031           0 :         else if (keyIsDirectBelow (parentKey, newKey))
    1032             :         {
    1033           0 :                 if (!hasGlobalRoot (parentKey, newKS))
    1034             :                 {
    1035           0 :                         createGlobalRoot (parentKey, newKS);
    1036           0 :                         keyAddName (sectionKey, INTERNAL_ROOT_SECTION);
    1037             :                 }
    1038             :                 else
    1039             :                 {
    1040           0 :                         keyDel (sectionKey);
    1041           0 :                         sectionKey = getGlobalRoot (parentKey, newKS);
    1042             :                 }
    1043           0 :                 keyDel (newKey);
    1044           0 :                 newKey = keyDup (sectionKey);
    1045           0 :                 keyAddBaseName (newKey, keyBaseName (cur));
    1046             :         }
    1047          11 : }
    1048             : 
    1049        7939 : void insertIntoKS (Key * parentKey, Key * cur, KeySet * newKS, IniPluginConfig * pluginConfig)
    1050             : {
    1051        7939 :         Key * appendKey = keyNew (keyName (parentKey), KEY_END);
    1052        7939 :         Key * sectionKey = keyDup (appendKey);
    1053        7939 :         Key * newKey = NULL;
    1054        7939 :         keySetName (sectionKey, keyName (cur));
    1055        7939 :         keySetMeta (sectionKey, "internal/ini/section", "");
    1056        7939 :         if (!keyGetMeta (cur, "internal/ini/section") && !keyIsBinary (cur))
    1057             :         {
    1058        7901 :                 keyAddName (sectionKey, "..");
    1059        7901 :                 if (keyIsDirectBelow (parentKey, cur))
    1060             :                 {
    1061          82 :                         if (!hasGlobalRoot (parentKey, newKS))
    1062             :                         {
    1063          76 :                                 createGlobalRoot (parentKey, newKS);
    1064          76 :                                 keyAddName (sectionKey, INTERNAL_ROOT_SECTION);
    1065             :                         }
    1066             :                         else
    1067             :                         {
    1068           6 :                                 keyDel (sectionKey);
    1069           6 :                                 sectionKey = getGlobalRoot (parentKey, newKS);
    1070             :                         }
    1071             :                 }
    1072        7901 :                 newKey = keyDup (sectionKey);
    1073        7901 :                 keyAddBaseName (newKey, keyBaseName (cur));
    1074        7901 :                 keySetMeta (newKey, "internal/ini/section", 0);
    1075        7901 :                 keyCopyAllMeta (newKey, cur);
    1076        7901 :                 keySetString (newKey, keyString (cur));
    1077             :         }
    1078             :         else
    1079             :         {
    1080          38 :                 keyCopyAllMeta (sectionKey, cur);
    1081             :         }
    1082        7939 :         if (pluginConfig->sectionHandling == ALWAYS || keyGetMeta (cur, "internal/ini/section") || keyIsBinary (cur))
    1083             :         {
    1084        7929 :                 if (!ksLookup (newKS, sectionKey, KDB_O_NONE))
    1085             :                 {
    1086         807 :                         keySetMeta (sectionKey, "internal/ini/section", "");
    1087         807 :                         ksAppendKey (newKS, sectionKey);
    1088         807 :                         insertKeyIntoKeySet (parentKey, sectionKey, newKS);
    1089             :                 }
    1090             :                 else
    1091             :                 {
    1092        7122 :                         keyDel (sectionKey);
    1093             :                 }
    1094             :         }
    1095             :         else
    1096             :         {
    1097          10 :                 keyDel (sectionKey);
    1098             :         }
    1099        7939 :         if (newKey)
    1100             :         {
    1101        7901 :                 if ((elektraArrayValidateName (newKey) == 1) && pluginConfig->array)
    1102             :                 {
    1103          11 :                         arrayHandler (parentKey, newKey, cur, sectionKey, newKS);
    1104             :                 }
    1105             :                 else
    1106             :                 {
    1107        7890 :                         ksAppendKey (newKS, newKey);
    1108        7890 :                         insertKeyIntoKeySet (parentKey, newKey, newKS);
    1109             :                 }
    1110             :         }
    1111        7939 :         keyDel (appendKey);
    1112        7939 : }
    1113             : 
    1114       51451 : static int iniCmpOrder (const void * a, const void * b)
    1115             : {
    1116       51451 :         const Key * ka = (*(const Key **) a);
    1117       51451 :         const Key * kb = (*(const Key **) b);
    1118             : 
    1119       51451 :         if (!ka && !kb) return 0;
    1120       51451 :         if (ka && !kb) return 1;
    1121       51451 :         if (!ka && kb) return -1;
    1122             : 
    1123       51451 :         const Key * kaom = keyGetMeta (ka, "internal/ini/order");
    1124       51451 :         const Key * kbom = keyGetMeta (kb, "internal/ini/order");
    1125       51451 :         const Key * kakm = keyGetMeta (ka, "internal/ini/key/number");
    1126       51451 :         const Key * kbkm = keyGetMeta (kb, "internal/ini/key/number");
    1127             : 
    1128       51451 :         int ret = keyGetNamespace (ka) - keyGetNamespace (kb);
    1129       51451 :         if (!ret)
    1130             :         {
    1131       51451 :                 if (!kaom && !kbom) return 0;
    1132       51451 :                 if (kaom && !kbom) return 1;
    1133       51451 :                 if (!kaom && kbom) return -1;
    1134       51451 :                 ret = strcmp (keyString (kaom), keyString (kbom));
    1135             :         }
    1136       51451 :         if (!ret)
    1137             :         {
    1138       34222 :                 if (!kakm && kbkm) return -1;
    1139       30787 :                 if (!kakm && !kbkm) return strcmp (keyName (ka), keyName (kb));
    1140       30759 :                 if (kakm && !kbkm) return 1;
    1141       30742 :                 if (kakm && kbkm) ret = strcmp (keyString (kakm), keyString (kbkm));
    1142             :         }
    1143             : 
    1144             :         return ret;
    1145             : }
    1146             : 
    1147             : // test if the keyname contains a reserved character and needs to be quoted
    1148       15717 : static int keyContainsSpecialCharacter (const char * str)
    1149             : {
    1150       15717 :         char * ptr = (char *) str;
    1151       15717 :         if (isspace (*ptr) || (isspace (*(ptr + strlen (str) - 1)))) return 1;
    1152       15717 :         if (*ptr == '#' || *ptr == ';') return 1;
    1153       15608 :         if (*ptr == '[') return 1;
    1154      569016 :         while (*ptr)
    1155             :         {
    1156      553409 :                 if (*ptr == '"' || *ptr == '=')
    1157             :                 {
    1158             :                         return 1;
    1159             :                 }
    1160      553408 :                 ++ptr;
    1161             :         }
    1162             :         return 0;
    1163             : }
    1164             : 
    1165             : // test if the keyvalue contains a reserved character and needs to be quoted
    1166        5923 : static int valueContainsSpecialCharacter (const char * str)
    1167             : {
    1168        5923 :         char * ptr = (char *) str;
    1169        5923 :         if (isspace (*ptr) || (isspace (*(ptr + strlen (str) - 1)))) return 1;
    1170        5632 :         if (*ptr == '#' || *ptr == ';') return 1;
    1171      141642 :         while (*ptr)
    1172             :         {
    1173      136420 :                 if (*ptr == '"' || *ptr == '=')
    1174             :                 {
    1175             :                         return 1;
    1176             :                 }
    1177      136026 :                 ++ptr;
    1178             :         }
    1179             :         return 0;
    1180             : }
    1181             : 
    1182       18390 : static void iniWriteMeta (FILE * fh, Key * key)
    1183             : {
    1184       18390 :         keyRewindMeta (key);
    1185      101058 :         while (keyNextMeta (key) != NULL)
    1186             :         {
    1187       64278 :                 Key * meta = (Key *) keyCurrentMeta (key);
    1188       64278 :                 const char * name = keyName (meta);
    1189       64536 :                 if (strncmp (name, "internal/", 9) && strcmp (name, "internal/ini/section") && strncmp (name, "comment", 7) &&
    1190         516 :                     strncmp (name, "warnings/", 9) && strncmp (name, "error/", 6) && strcmp (name, "warnings") && strcmp (name, "error"))
    1191             :                 {
    1192         258 :                         if (!strcmp (name, "binary") && keyGetNamespace (key) != KEY_NS_SPEC) continue;
    1193         247 :                         const char * string = keyString (meta);
    1194         247 :                         fprintf (fh, "#@META %s = %s\n", name, string);
    1195             :                 }
    1196             :         }
    1197       18390 : }
    1198             : 
    1199             : 
    1200        1269 : static int iniWriteKeySet (FILE * fh, Key * parentKey, KeySet * returned, IniPluginConfig * config)
    1201             : {
    1202        1269 :         ksRewind (returned);
    1203             :         Key ** keyArray;
    1204        1269 :         ssize_t arraySize = ksGetSize (returned);
    1205        1269 :         if (arraySize == 0) return 0;
    1206        1238 :         keyArray = elektraCalloc (arraySize * sizeof (Key *));
    1207        1238 :         elektraKsToMemArray (returned, keyArray);
    1208        1238 :         qsort (keyArray, arraySize, sizeof (Key *), iniCmpOrder);
    1209        1238 :         Key * cur = NULL;
    1210        1238 :         Key * sectionKey = parentKey;
    1211        1238 :         int ret = 1;
    1212        1238 :         const char delim = config->delim;
    1213             : 
    1214        1238 :         int removeSectionKey = 0;
    1215             : 
    1216       19628 :         for (ssize_t i = 0; i < arraySize; ++i)
    1217             :         {
    1218       18390 :                 cur = keyArray[i];
    1219             : 
    1220       18390 :                 if (!strcmp (keyName (parentKey), keyName (cur)))
    1221             :                 {
    1222         969 :                         continue;
    1223             :                 }
    1224       17421 :                 if (keyName (parentKey)[0] == '/')
    1225             :                 {
    1226           5 :                         if (!strchr (keyName (cur) + 1, '/')) continue;
    1227           5 :                         if (!strcmp (keyName (parentKey), strchr (keyName (cur) + 1, '/'))) continue;
    1228             :                 }
    1229             : 
    1230             : 
    1231       17420 :                 if (isSectionKey (cur))
    1232             :                 {
    1233        1710 :                         if (removeSectionKey)
    1234             :                         {
    1235           0 :                                 keyDel (sectionKey);
    1236           0 :                                 sectionKey = parentKey;
    1237           0 :                                 removeSectionKey = 0;
    1238             :                         }
    1239             :                         else
    1240             :                         {
    1241             :                                 sectionKey = cur;
    1242             :                         }
    1243             :                 }
    1244       17420 :                 writeComments (cur, fh, config->commentChar);
    1245       17420 :                 iniWriteMeta (fh, cur);
    1246       17420 :                 if (config->sectionHandling == NONE)
    1247             :                 {
    1248           0 :                         char * name = getIniName (parentKey, cur);
    1249           0 :                         if (!keyGetMeta (cur, "internal/ini/section"))
    1250             :                         {
    1251           0 :                                 const char * string = keyString (cur);
    1252           0 :                                 if (keyContainsSpecialCharacter (name))
    1253           0 :                                         fprintf (fh, ININAME_DELIM_SPECIAL_MASK, name, delim);
    1254             :                                 else
    1255           0 :                                         fprintf (fh, ININAME_DELIM_MASK, name, delim);
    1256           0 :                                 if (strlen (string) && (valueContainsSpecialCharacter (string)))
    1257           0 :                                         fprintf (fh, "\"%s\"\n", string);
    1258             :                                 else
    1259           0 :                                         fprintf (fh, "%s\n", string);
    1260             :                         }
    1261           0 :                         free (name);
    1262             :                 }
    1263             :                 else
    1264             :                 {
    1265       17420 :                         if (isSectionKey (cur) && keyGetValueSize (cur) <= 1)
    1266             :                         {
    1267        1710 :                                 if (keyIsBelow (parentKey, cur))
    1268             :                                 {
    1269        1710 :                                         char * iniName = getIniName (parentKey, cur);
    1270        1710 :                                         fprintf (fh, "[%s]\n", iniName);
    1271        1710 :                                         free (iniName);
    1272             :                                 }
    1273             :                                 else
    1274             :                                 {
    1275           0 :                                         if (removeSectionKey)
    1276             :                                         {
    1277           0 :                                                 keyDel (sectionKey);
    1278           0 :                                                 removeSectionKey = 0;
    1279             :                                         }
    1280           0 :                                         sectionKey = parentKey;
    1281           0 :                                         fprintf (fh, "[]\n");
    1282             :                                 }
    1283             :                         }
    1284             :                         else
    1285             :                         {
    1286             :                                 // handle possible section conflicts
    1287       15710 :                                 const Key * parentMeta = keyGetMeta (cur, "internal/ini/parent");
    1288       15710 :                                 if (parentMeta && !keyIsBelow (sectionKey, cur))
    1289             :                                 {
    1290          14 :                                         Key * oldSectionKey = sectionKey;
    1291          14 :                                         sectionKey = keyNew (keyString (parentMeta), KEY_END);
    1292          14 :                                         if (!keyIsBelow (oldSectionKey, sectionKey))
    1293             :                                         {
    1294          14 :                                                 if (keyIsBelow (parentKey, sectionKey))
    1295             :                                                 {
    1296          13 :                                                         char * name = getIniName (parentKey, sectionKey);
    1297          13 :                                                         fprintf (fh, "[%s]\n", name);
    1298          13 :                                                         elektraFree (name);
    1299             :                                                 }
    1300           1 :                                                 else if (!strcmp (keyName (parentKey), "/") && !strcmp (keyString (parentMeta), "/"))
    1301             :                                                 {
    1302           0 :                                                         fprintf (fh, "[]\n");
    1303             :                                                 }
    1304           1 :                                                 else if (!strcmp (keyName (parentKey), "/"))
    1305             :                                                 {
    1306           0 :                                                         fprintf (fh, "[%s]\n", keyString (parentMeta));
    1307             :                                                 }
    1308           1 :                                                 else if (!strcmp (keyName (sectionKey), keyName (parentKey)))
    1309             :                                                 {
    1310           1 :                                                         fprintf (fh, "[]\n");
    1311             :                                                 }
    1312           0 :                                                 else if (keyGetNamespace (sectionKey) != keyGetNamespace (oldSectionKey))
    1313             :                                                 {
    1314           0 :                                                         if (keyIsBelow (parentKey, sectionKey))
    1315             :                                                         {
    1316           0 :                                                                 char * name = getIniName (parentKey, sectionKey);
    1317           0 :                                                                 fprintf (fh, "[%s]\n", name);
    1318           0 :                                                                 elektraFree (name);
    1319             :                                                         }
    1320             :                                                         else
    1321           0 :                                                                 fprintf (fh, "[]\n");
    1322             :                                                 }
    1323             : 
    1324          14 :                                                 if (removeSectionKey)
    1325             :                                                 {
    1326             :                                                         // remove previous sectionKey
    1327          11 :                                                         keyDel (oldSectionKey);
    1328             :                                                 }
    1329             : 
    1330             :                                                 // mark allocated sectionKey to be freed later
    1331             :                                                 removeSectionKey = 1;
    1332             :                                         }
    1333             :                                         else
    1334             :                                         {
    1335           0 :                                                 keyDel (sectionKey);
    1336           0 :                                                 sectionKey = oldSectionKey;
    1337           0 :                                                 removeSectionKey = 0;
    1338             :                                         }
    1339             :                                 }
    1340       15710 :                                 if (keyGetMeta (cur, "internal/ini/array") && config->array)
    1341           4 :                                 {
    1342           8 :                                         int lastArrayIndex = atoi (keyString (keyGetMeta (cur, "internal/ini/array")) + 1);
    1343           4 :                                         keySetBaseName (cur, 0);
    1344             : 
    1345           4 :                                         char * name = getIniName (sectionKey, cur);
    1346           4 :                                         ++i;
    1347          13 :                                         for (int j = i; j <= i + lastArrayIndex; ++j)
    1348             :                                         {
    1349          13 :                                                 cur = keyArray[j];
    1350          13 :                                                 const char * string = keyString (cur);
    1351          13 :                                                 if (keyContainsSpecialCharacter (name))
    1352           0 :                                                         fprintf (fh, ININAME_DELIM_SPECIAL_MASK, name, delim);
    1353             :                                                 else
    1354          13 :                                                         fprintf (fh, ININAME_DELIM_MASK, name, delim);
    1355          13 :                                                 if (strlen (string) && (valueContainsSpecialCharacter (string)))
    1356           0 :                                                         fprintf (fh, "\"%s\"\n", string);
    1357             :                                                 else
    1358          13 :                                                         fprintf (fh, "%s\n", string);
    1359          26 :                                                 if (lastArrayIndex == atoi (keyBaseName (cur) + 1))
    1360             :                                                 {
    1361           4 :                                                         lastArrayIndex = j - i;
    1362           4 :                                                         break;
    1363             :                                                 }
    1364             :                                         }
    1365           4 :                                         free (name);
    1366           4 :                                         i += lastArrayIndex;
    1367             :                                 }
    1368             :                                 else
    1369             :                                 {
    1370             :                                         char * name;
    1371       15706 :                                         if (keyIsBelow (sectionKey, cur) && keyIsBelow (parentKey, cur))
    1372             :                                         {
    1373       15706 :                                                 if (keyIsBelow (parentKey, sectionKey))
    1374             :                                                 {
    1375       15380 :                                                         name = getIniName (sectionKey, cur);
    1376             :                                                 }
    1377             :                                                 else
    1378             :                                                 {
    1379         326 :                                                         name = getIniName (parentKey, cur);
    1380             :                                                 }
    1381             :                                         }
    1382           0 :                                         else if (keyIsBelow (sectionKey, cur))
    1383             :                                         {
    1384           0 :                                                 name = getIniName (sectionKey, cur);
    1385             :                                         }
    1386             :                                         else
    1387             :                                         {
    1388           0 :                                                 name = getIniName (parentKey, cur);
    1389             :                                         }
    1390             : 
    1391       15706 :                                         if (keyGetMeta (cur, "internal/ini/empty"))
    1392             :                                         {
    1393           2 :                                                 if (keyContainsSpecialCharacter (name))
    1394           0 :                                                         fprintf (fh, "\"%s\"\n", name);
    1395             :                                                 else
    1396           2 :                                                         fprintf (fh, "%s\n", name);
    1397             :                                         }
    1398       15704 :                                         else if (strstr (keyString (cur), "\n") == 0)
    1399             :                                         {
    1400       15694 :                                                 const char * string = keyString (cur);
    1401       15694 :                                                 if (keyContainsSpecialCharacter (name))
    1402         110 :                                                         fprintf (fh, ININAME_DELIM_SPECIAL_MASK, name, delim);
    1403             :                                                 else
    1404       15584 :                                                         fprintf (fh, ININAME_DELIM_MASK, name, delim);
    1405       15694 :                                                 if (strlen (string) && (valueContainsSpecialCharacter (string)))
    1406          20 :                                                         fprintf (fh, "\"%s\"\n", string);
    1407       15674 :                                                 else if (strlen (string))
    1408        4519 :                                                         fprintf (fh, "%s\n", string);
    1409             :                                                 else
    1410       11155 :                                                         fprintf (fh, "\n");
    1411             :                                         }
    1412             :                                         else
    1413             :                                         {
    1414          10 :                                                 if (config->supportMultiline)
    1415             :                                                 {
    1416           8 :                                                         writeMultilineKey (cur, name, fh, config);
    1417             :                                                 }
    1418             :                                                 else
    1419             :                                                 {
    1420           2 :                                                         ELEKTRA_SET_INSTALLATION_ERROR (
    1421             :                                                                 parentKey,
    1422             :                                                                 "Encountered a multiline value but multiline support is not enabled. "
    1423             :                                                                 "Have a look at kdb info ini for more details");
    1424           2 :                                                         ret = -1;
    1425             :                                                 }
    1426             :                                         }
    1427       15706 :                                         free (name);
    1428             :                                 }
    1429             :                         }
    1430             :                 }
    1431             :         }
    1432        1238 :         if (config->lastComments)
    1433             :         {
    1434           0 :                 writeComments (config->lastComments, fh, config->commentChar);
    1435             :         }
    1436        1238 :         if (removeSectionKey) keyDel (sectionKey);
    1437             : 
    1438        1238 :         elektraFree (keyArray);
    1439        1238 :         return ret;
    1440             : }
    1441       18268 : static void stripInternalData (Key * parentKey ELEKTRA_UNUSED, KeySet * ks)
    1442             : {
    1443       18268 :         ksRewind (ks);
    1444             :         Key * cur;
    1445       18268 :         KeySet * newKS = ksNew (ksGetSize (ks), KS_END);
    1446      206532 :         while ((cur = ksNext (ks)) != NULL)
    1447             :         {
    1448      169996 :                 if (!strcmp (keyBaseName (cur), INTERNAL_ROOT_SECTION))
    1449             :                 {
    1450         324 :                         keyDel (cur);
    1451         324 :                         continue;
    1452             :                 }
    1453      169672 :                 if (strstr (keyName (cur), INTERNAL_ROOT_SECTION))
    1454             :                 {
    1455        1041 :                         Key * newKey = keyDup (cur);
    1456        1041 :                         char * oldName = elektraStrDup (keyName (cur));
    1457        1041 :                         char * newName = elektraCalloc (elektraStrLen (keyName (cur)));
    1458        1041 :                         char * token = strtok (oldName, "/");
    1459        1041 :                         strcat (newName, token);
    1460        7641 :                         while (token != NULL)
    1461             :                         {
    1462        6600 :                                 token = strtok (NULL, "/");
    1463        6600 :                                 if (token == NULL) break;
    1464        5559 :                                 if (!strcmp (token, INTERNAL_ROOT_SECTION)) continue;
    1465        4518 :                                 strcat (newName, "/");
    1466        4518 :                                 strcat (newName, token);
    1467             :                         }
    1468        1041 :                         strcat (newName, "/");
    1469        1041 :                         keySetName (newKey, newName);
    1470        1041 :                         char * parent = findParent (parentKey, newKey, ksDup (newKS));
    1471        1041 :                         keySetMeta (newKey, "internal/ini/parent", parent);
    1472        1041 :                         elektraFree (parent);
    1473        1041 :                         if (strcmp (keyName (parentKey), keyName (newKey))) ksAppendKey (newKS, keyDup (newKey));
    1474        1041 :                         keyDel (newKey);
    1475        1041 :                         elektraFree (oldName);
    1476        1041 :                         elektraFree (newName);
    1477             :                 }
    1478             :                 else
    1479             :                 {
    1480      168631 :                         if (!ksLookup (newKS, cur, KDB_O_NONE)) ksAppendKey (newKS, cur);
    1481             :                 }
    1482             :         }
    1483       18268 :         ksClear (ks);
    1484       18268 :         ksAppend (ks, newKS);
    1485       18268 :         ksDel (newKS);
    1486       18268 : }
    1487             : 
    1488         525 : static void removeLeftoverSections (KeySet * ks, KeySet * oldKS, Key * parentKey)
    1489             : {
    1490             :         Key * cur;
    1491         525 :         KeySet * removedKeys = ksNew (0, KS_END);
    1492         525 :         ksRewind (oldKS);
    1493       14079 :         while ((cur = ksNext (oldKS)) != NULL)
    1494             :         {
    1495       13029 :                 Key * lookup = ksLookup (ks, cur, KDB_O_NONE);
    1496       13029 :                 if (!lookup)
    1497             :                 {
    1498        8106 :                         ksAppendKey (removedKeys, cur);
    1499             :                 }
    1500             :         }
    1501         525 :         ksRewind (removedKeys);
    1502        9156 :         while ((cur = ksPop (removedKeys)) != NULL)
    1503             :         {
    1504        8106 :                 const Key * parentMeta = keyGetMeta (cur, "internal/ini/parent");
    1505        8106 :                 if (parentMeta && strcmp (keyName (parentKey), keyString (parentMeta)))
    1506             :                 {
    1507        8055 :                         Key * sectionKey = ksLookupByName (ks, keyString (parentMeta), KDB_O_NONE);
    1508        8055 :                         if (sectionKey)
    1509             :                         {
    1510        3130 :                                 KeySet * cutKS = ksCut (ks, sectionKey);
    1511             :                                 Key * k;
    1512        3130 :                                 int empty = 1;
    1513        3130 :                                 ksRewind (cutKS);
    1514       12196 :                                 while (((k = ksNext (cutKS)) != NULL) && empty)
    1515             :                                 {
    1516        5936 :                                         const Key * meta = keyGetMeta (k, "internal/ini/parent");
    1517        5936 :                                         if (meta && !strcmp (keyString (meta), keyName (sectionKey)))
    1518             :                                         {
    1519        2806 :                                                 empty = 0;
    1520             :                                         }
    1521             :                                 }
    1522        3130 :                                 if (empty)
    1523             :                                 {
    1524         324 :                                         keyDel (ksLookup (cutKS, sectionKey, KDB_O_POP));
    1525             :                                 }
    1526        3130 :                                 ksAppend (ks, cutKS);
    1527        3130 :                                 ksDel (cutKS);
    1528             :                         }
    1529             :                 }
    1530        8106 :                 keyDel (cur);
    1531             :         }
    1532         525 :         ksDel (removedKeys);
    1533         525 : }
    1534             : 
    1535        1269 : int elektraIniSet (Plugin * handle, KeySet * returned, Key * parentKey)
    1536             : {
    1537             :         /* set all keys */
    1538        1269 :         int errnosave = errno;
    1539        1269 :         int ret = 1;
    1540        1269 :         FILE * fh = fopen (keyString (parentKey), "w");
    1541             : 
    1542        1269 :         if (!fh)
    1543             :         {
    1544           0 :                 ELEKTRA_SET_ERROR_SET (parentKey);
    1545           0 :                 errno = errnosave;
    1546           0 :                 return -1;
    1547             :         }
    1548        1269 :         Key * root = keyDup (ksLookup (returned, parentKey, KDB_O_NONE));
    1549        1269 :         Key * head = keyDup (ksHead (returned));
    1550        1269 :         IniPluginConfig * pluginConfig = elektraPluginGetData (handle);
    1551        1269 :         if (pluginConfig->lastOrder && !keyGetMeta (parentKey, "internal/ini/order"))
    1552             :         {
    1553         262 :                 keySetMeta (parentKey, "internal/ini/order", pluginConfig->lastOrder);
    1554         262 :                 incOrder (parentKey);
    1555             :         }
    1556        1007 :         else if (!keyGetMeta (parentKey, "internal/ini/order"))
    1557             :         {
    1558          62 :                 keySetMeta (parentKey, "internal/ini/order", "#1");
    1559             :         }
    1560             :         else
    1561             :         {
    1562         945 :                 incOrder (parentKey);
    1563             :         }
    1564             :         Key * cur;
    1565        1269 :         KeySet * newKS = ksNew (0, KS_END);
    1566        1269 :         ksRewind (returned);
    1567       31742 :         while ((cur = ksNext (returned)) != NULL)
    1568             :         {
    1569       29204 :                 if (keyGetMeta (cur, "internal/ini/order"))
    1570             :                 {
    1571       10023 :                         ksAppendKey (newKS, cur);
    1572       10023 :                         keyDel (ksLookup (returned, cur, KDB_O_POP));
    1573             :                 }
    1574             :         }
    1575        1269 :         ksRewind (returned);
    1576             : 
    1577       10478 :         while ((cur = ksNext (returned)) != NULL)
    1578             :         {
    1579        7940 :                 if (!strcmp (keyName (cur), keyName (parentKey))) continue;
    1580        7939 :                 if (!strcmp (keyBaseName (cur), INTERNAL_ROOT_SECTION)) continue;
    1581        7939 :                 insertIntoKS (parentKey, cur, newKS, pluginConfig);
    1582        7939 :                 keyDel (ksLookup (returned, cur, KDB_O_POP));
    1583             :         }
    1584        1269 :         ksClear (returned);
    1585        1269 :         ksAppend (returned, newKS);
    1586        1269 :         ksDel (newKS);
    1587        1269 :         setParents (returned, parentKey);
    1588        1269 :         stripInternalData (parentKey, returned);
    1589        1269 :         if (pluginConfig->BOM == 1)
    1590             :         {
    1591           0 :                 fprintf (fh, "\xEF\xBB\xBF");
    1592             :         }
    1593             : 
    1594        1269 :         int rootNeededSync = 0;
    1595        1269 :         if (keyNeedSync (parentKey) && root)
    1596             :         {
    1597         970 :                 if (strncmp (keyString (parentKey), keyString (root), strlen (keyString (root))))
    1598             :                 {
    1599           0 :                         if (keyString (root) && strlen (keyString (root)))
    1600             :                         {
    1601           0 :                                 iniWriteMeta (fh, root);
    1602           0 :                                 fprintf (fh, "= %s\n", keyString (root));
    1603           0 :                                 rootNeededSync = 1;
    1604             :                         }
    1605             :                 }
    1606             :                 else
    1607             :                 {
    1608         970 :                         iniWriteMeta (fh, root);
    1609         970 :                         fprintf (fh, "[]\n");
    1610         970 :                         rootNeededSync = 1;
    1611             :                 }
    1612             :         }
    1613        1269 :         keyDel (root);
    1614        1269 :         keyDel (head);
    1615             : 
    1616        1269 :         KeySet * oldKS = pluginConfig->oldKS;
    1617        1269 :         if ((pluginConfig->sectionHandling == ALWAYS) && (ksGetSize (returned) < ksGetSize (oldKS)))
    1618             :         {
    1619         525 :                 removeLeftoverSections (returned, oldKS, parentKey);
    1620         525 :                 ksDel (pluginConfig->oldKS);
    1621         525 :                 pluginConfig->oldKS = NULL;
    1622         525 :                 elektraPluginSetData (handle, pluginConfig);
    1623             :         }
    1624        1269 :         ret = iniWriteKeySet (fh, parentKey, returned, pluginConfig);
    1625             : 
    1626        1269 :         fclose (fh);
    1627        1269 :         errno = errnosave;
    1628        1269 :         if (pluginConfig->lastOrder) elektraFree (pluginConfig->lastOrder);
    1629        1269 :         pluginConfig->lastOrder = elektraStrDup (keyString (keyGetMeta (parentKey, "internal/ini/order")));
    1630        1269 :         elektraPluginSetData (handle, pluginConfig);
    1631             : 
    1632        1269 :         if (ret == 0 && rootNeededSync) return ELEKTRA_PLUGIN_STATUS_SUCCESS;
    1633             : 
    1634        1268 :         return ret; /* success */
    1635             : }
    1636             : 
    1637       45393 : Plugin * ELEKTRA_PLUGIN_EXPORT
    1638             : {
    1639             :         // clang-format off
    1640       45393 :     return elektraPluginExport ("ini",
    1641             :             ELEKTRA_PLUGIN_OPEN, &elektraIniOpen,
    1642             :             ELEKTRA_PLUGIN_CLOSE, &elektraIniClose,
    1643             :             ELEKTRA_PLUGIN_GET, &elektraIniGet,
    1644             :             ELEKTRA_PLUGIN_SET, &elektraIniSet,
    1645             :             ELEKTRA_PLUGIN_END);
    1646             : }

Generated by: LCOV version 1.13