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 : }
|