Line data Source code
1 : /*
2 : INI LIBRARY
3 :
4 : Module represents interface to the main INI object.
5 :
6 : Copyright (C) Dmitri Pal <dpal@redhat.com> 2010
7 :
8 : INI Library is free software: you can redistribute it and/or modify
9 : it under the terms of the GNU Lesser General Public License as published by
10 : the Free Software Foundation, either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : INI Library is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU Lesser General Public License for more details.
17 :
18 : You should have received a copy of the GNU Lesser General Public License
19 : along with INI Library. If not, see <http://www.gnu.org/licenses/>.
20 : */
21 :
22 : #include "config.h"
23 : #include <errno.h>
24 : #include <stdio.h>
25 : #include <string.h>
26 : #include <stdint.h>
27 : #include <stdlib.h>
28 : /* For error text */
29 : #include <libintl.h>
30 : #define _(String) gettext (String)
31 : #include "trace.h"
32 : #include "collection.h"
33 : #include "collection_tools.h"
34 : #include "ini_configobj.h"
35 : #include "ini_config_priv.h"
36 : #include "ini_defines.h"
37 : #include "ini_valueobj.h"
38 : #include "ini_configobj.h"
39 :
40 : /* Internal structure used during the merge operation */
41 : struct merge_data {
42 : struct collection_item *ci;
43 : uint32_t flags;
44 : int error;
45 : int found;
46 : };
47 :
48 : /* Callback */
49 139360 : void ini_cleanup_cb(const char *property,
50 : int property_len,
51 : int type,
52 : void *data,
53 : int length,
54 : void *custom_data)
55 : {
56 139360 : struct value_obj *vo = NULL;
57 :
58 : TRACE_FLOW_ENTRY();
59 : TRACE_INFO_STRING("Cleaning ", property);
60 :
61 : /* Banary items are the values */
62 139360 : if(type == COL_TYPE_BINARY) {
63 133473 : vo = *((struct value_obj **)(data));
64 133473 : value_destroy(vo);
65 : }
66 :
67 : TRACE_FLOW_EXIT();
68 139360 : }
69 :
70 : /* Clean the search state */
71 501 : void ini_config_clean_state(struct ini_cfgobj *ini_config)
72 : {
73 : TRACE_FLOW_ENTRY();
74 :
75 501 : if (ini_config) {
76 501 : if (ini_config->iterator) col_unbind_iterator(ini_config->iterator);
77 501 : ini_config->iterator = NULL;
78 501 : free(ini_config->section);
79 501 : ini_config->section = NULL;
80 501 : free(ini_config->name);
81 501 : ini_config->name = NULL;
82 501 : ini_config->section_len = 0;
83 501 : ini_config->name_len = 0;
84 : }
85 :
86 : TRACE_FLOW_EXIT();
87 501 : }
88 :
89 :
90 :
91 : /* Traverse the collection and clean the object */
92 425 : void ini_config_destroy(struct ini_cfgobj *ini_config)
93 : {
94 : TRACE_FLOW_ENTRY();
95 :
96 425 : ini_config_clean_state(ini_config);
97 :
98 425 : if (ini_config) {
99 425 : if (ini_config->cfg) {
100 :
101 425 : col_destroy_collection_with_cb(ini_config->cfg,
102 : ini_cleanup_cb,
103 : NULL);
104 : }
105 425 : if (ini_config->last_comment) {
106 186 : ini_comment_destroy(ini_config->last_comment);
107 : }
108 :
109 425 : if (ini_config->error_list) {
110 246 : col_destroy_collection(ini_config->error_list);
111 : }
112 :
113 425 : free(ini_config);
114 : }
115 :
116 : TRACE_FLOW_EXIT();
117 425 : }
118 :
119 : /* Create a config object */
120 246 : int ini_config_create(struct ini_cfgobj **ini_config)
121 : {
122 246 : int error = EOK;
123 246 : struct ini_cfgobj *new_co = NULL;
124 :
125 : TRACE_FLOW_ENTRY();
126 :
127 246 : if (!ini_config) {
128 : TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
129 : return EINVAL;
130 : }
131 :
132 246 : new_co = malloc(sizeof(struct ini_cfgobj));
133 246 : if (!new_co) {
134 : TRACE_ERROR_NUMBER("Failed to allocate memory", ENOMEM);
135 : return ENOMEM;
136 : }
137 :
138 246 : new_co->cfg = NULL;
139 246 : new_co->boundary = INI_WRAP_BOUNDARY;
140 246 : new_co->last_comment = NULL;
141 246 : new_co->section = NULL;
142 246 : new_co->name = NULL;
143 246 : new_co->section_len = 0;
144 246 : new_co->name_len = 0;
145 246 : new_co->iterator = NULL;
146 246 : new_co->error_list = NULL;
147 246 : new_co->count = 0;
148 :
149 : /* Create a collection to hold configuration data */
150 246 : error = col_create_collection(&(new_co->cfg),
151 : INI_CONFIG_NAME,
152 : COL_CLASS_INI_CONFIG);
153 246 : if (error != EOK) {
154 : TRACE_ERROR_NUMBER("Failed to create collection.", error);
155 0 : ini_config_destroy(new_co);
156 0 : return error;
157 : }
158 :
159 : /* Create error list collection */
160 246 : error = col_create_collection(&(new_co->error_list),
161 : INI_ERROR,
162 : COL_CLASS_INI_PERROR);
163 246 : if (error) {
164 : TRACE_ERROR_NUMBER("Failed to create error list", error);
165 0 : ini_config_destroy(new_co);
166 0 : return error;
167 : }
168 :
169 246 : *ini_config = new_co;
170 :
171 : TRACE_FLOW_EXIT();
172 246 : return error;
173 : }
174 :
175 : /* Callback to set the boundary */
176 68138 : static int ini_boundary_cb(const char *property,
177 : int property_len,
178 : int type,
179 : void *data,
180 : int length,
181 : void *custom_data,
182 : int *dummy)
183 : {
184 68138 : int error = EOK;
185 68138 : struct value_obj *vo = NULL;
186 : uint32_t boundary;
187 :
188 : TRACE_FLOW_ENTRY();
189 :
190 68138 : boundary = *((uint32_t *)(custom_data));
191 : /* Banary items are the values */
192 68138 : if(type == COL_TYPE_BINARY) {
193 65744 : vo = *((struct value_obj **)(data));
194 65744 : error = value_set_boundary(vo, boundary);
195 : }
196 :
197 : TRACE_FLOW_EXIT();
198 68138 : return error;
199 : }
200 :
201 : /* Set the folding boundary for multiline values.
202 : * Use before serializing and saving to a file if the
203 : * default boundary of 80 characters does not work for you.
204 : */
205 154 : int ini_config_set_wrap(struct ini_cfgobj *ini_config,
206 : uint32_t boundary)
207 : {
208 154 : int error = EOK;
209 :
210 : TRACE_FLOW_ENTRY();
211 :
212 154 : if (!ini_config) {
213 : TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
214 : return EINVAL;
215 : }
216 :
217 154 : ini_config->boundary = boundary;
218 154 : error = col_traverse_collection(ini_config->cfg,
219 : COL_TRAVERSE_DEFAULT,
220 : ini_boundary_cb,
221 154 : (void *)(&(ini_config->boundary)));
222 : if (error) {
223 : TRACE_ERROR_NUMBER("Failed to set wrapping boundary", error);
224 : return error;
225 : }
226 :
227 :
228 : TRACE_FLOW_EXIT();
229 : return error;
230 : }
231 :
232 : /* Configuration copy callback */
233 66243 : static int ini_copy_cb(struct collection_item *item,
234 : void *ext_data,
235 : int *skip)
236 : {
237 66243 : int error = EOK;
238 66243 : struct value_obj *vo = NULL;
239 66243 : struct value_obj *new_vo = NULL;
240 :
241 : TRACE_FLOW_ENTRY();
242 :
243 66243 : *skip = 0;
244 :
245 : /* Binary items are the values */
246 66243 : if(col_get_item_type(item) == COL_TYPE_BINARY) {
247 66243 : vo = *((struct value_obj **)(col_get_item_data(item)));
248 :
249 66243 : error = value_copy(vo, &new_vo);
250 66243 : if (error) {
251 : TRACE_ERROR_NUMBER("Failed to copy value", error);
252 : return error;
253 : }
254 :
255 66243 : error = col_modify_binary_item(item,
256 : NULL,
257 : &new_vo,
258 : sizeof(struct value_obj *));
259 66243 : if (error) {
260 : TRACE_ERROR_NUMBER("Failed to copy value", error);
261 0 : value_destroy(new_vo);
262 0 : return error;
263 : }
264 : }
265 :
266 : TRACE_FLOW_EXIT();
267 66243 : return error;
268 : }
269 :
270 : /* Copy configuration */
271 179 : int ini_config_copy(struct ini_cfgobj *ini_config,
272 : struct ini_cfgobj **ini_new)
273 : {
274 179 : int error = EOK;
275 179 : struct ini_cfgobj *new_co = NULL;
276 :
277 : TRACE_FLOW_ENTRY();
278 :
279 358 : if ((!ini_config) ||
280 179 : (!ini_new)) {
281 : TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
282 : return EINVAL;
283 : }
284 :
285 : /* Create a new configuration object */
286 179 : new_co = malloc(sizeof(struct ini_cfgobj));
287 179 : if (!new_co) {
288 : TRACE_ERROR_NUMBER("Failed to allocate memory", ENOMEM);
289 : return ENOMEM;
290 : }
291 :
292 179 : new_co->cfg = NULL;
293 179 : new_co->boundary = ini_config->boundary;
294 179 : new_co->last_comment = NULL;
295 179 : new_co->section = NULL;
296 179 : new_co->name = NULL;
297 179 : new_co->section_len = 0;
298 179 : new_co->name_len = 0;
299 179 : new_co->iterator = NULL;
300 179 : new_co->error_list = NULL;
301 179 : new_co->count = 0;
302 :
303 179 : error = col_copy_collection_with_cb(&(new_co->cfg),
304 : ini_config->cfg,
305 : INI_CONFIG_NAME,
306 : COL_COPY_NORMAL,
307 : ini_copy_cb,
308 : NULL);
309 179 : if (error) {
310 : TRACE_ERROR_NUMBER("Failed to copy collection", error);
311 0 : ini_config_destroy(new_co);
312 0 : return error;
313 : }
314 :
315 179 : if (ini_config->last_comment) {
316 67 : error = ini_comment_copy(ini_config->last_comment,
317 : &(new_co->last_comment));
318 67 : if (error) {
319 : TRACE_ERROR_NUMBER("Failed to copy comment", error);
320 0 : ini_config_destroy(new_co);
321 0 : return error;
322 : }
323 : }
324 :
325 179 : *ini_new = new_co;
326 :
327 : TRACE_FLOW_EXIT();
328 179 : return error;
329 : }
330 :
331 :
332 : /* Callback to process merging of the sections */
333 181 : static int merge_section_handler(const char *property,
334 : int property_len,
335 : int type,
336 : void *data,
337 : int length,
338 : void *custom_data,
339 : int *dummy)
340 : {
341 181 : int error = EOK;
342 181 : struct value_obj *vo = NULL;
343 181 : struct value_obj *new_vo = NULL;
344 181 : struct value_obj *vo_old = NULL;
345 : struct merge_data *passed_data;
346 181 : struct collection_item *acceptor = NULL;
347 181 : struct collection_item *item = NULL;
348 : unsigned insertmode;
349 : uint32_t mergemode;
350 181 : int suppress = 0;
351 181 : int doinsert = 0;
352 :
353 : TRACE_FLOW_ENTRY();
354 :
355 181 : if ((type != COL_TYPE_BINARY) ||
356 153 : ((type == COL_TYPE_BINARY) &&
357 153 : (strncmp(property, INI_SECTION_KEY,
358 : sizeof(INI_SECTION_KEY)) == 0))) {
359 : /* Skip items we do not care about */
360 : TRACE_FLOW_EXIT();
361 : return EOK;
362 : }
363 :
364 : /* Get value */
365 125 : vo = *((struct value_obj **)(data));
366 :
367 : /* Copy it */
368 125 : error = value_copy(vo, &new_vo);
369 125 : if (error) {
370 : TRACE_ERROR_NUMBER("Failed to copy value", error);
371 : return error;
372 : }
373 :
374 125 : passed_data = (struct merge_data *)(custom_data);
375 125 : acceptor = passed_data->ci;
376 125 : mergemode = passed_data->flags & INI_MV2S_MASK;
377 :
378 125 : switch (mergemode) {
379 5 : case INI_MV2S_ERROR: insertmode = COL_INSERT_DUPERROR;
380 5 : doinsert = 1;
381 5 : break;
382 15 : case INI_MV2S_PRESERVE: insertmode = COL_INSERT_DUPERROR;
383 15 : doinsert = 1;
384 15 : suppress = 1;
385 15 : break;
386 45 : case INI_MV2S_ALLOW: insertmode = COL_INSERT_NOCHECK;
387 45 : doinsert = 1;
388 45 : break;
389 : case INI_MV2S_OVERWRITE: /* Special handling */
390 : case INI_MV2S_DETECT:
391 : default:
392 : break;
393 : }
394 :
395 : /* Do not insert but search for dups first */
396 125 : if (!doinsert) {
397 : TRACE_INFO_STRING("Overwrite mode. Looking for:",
398 : property);
399 :
400 60 : error = col_get_item(acceptor,
401 : property,
402 : COL_TYPE_BINARY,
403 : COL_TRAVERSE_DEFAULT,
404 : &item);
405 :
406 60 : if (error) {
407 : TRACE_ERROR_NUMBER("Failed searching for dup", error);
408 0 : value_destroy(new_vo);
409 0 : return error;
410 : }
411 :
412 : /* Check if there is a dup */
413 60 : if (item) {
414 : /* Check if we are in the detect mode */
415 46 : if (mergemode == INI_MV2S_DETECT) {
416 38 : passed_data->error = EEXIST;
417 38 : doinsert = 1;
418 38 : insertmode = COL_INSERT_NOCHECK;
419 : }
420 : else {
421 :
422 : /* We are in the OVERWRITE mode.
423 : * Dup exists - update it.
424 : */
425 8 : vo_old = *((struct value_obj **)(col_get_item_data(item)));
426 8 : error = col_modify_binary_item(item,
427 : NULL,
428 : &new_vo,
429 : sizeof(struct value_obj *));
430 8 : if (error) {
431 : TRACE_ERROR_NUMBER("Failed updating the value", error);
432 0 : value_destroy(new_vo);
433 0 : return error;
434 : }
435 :
436 : /* If we failed to update it is better to leak then crash,
437 : * so destroy original value only on the successful update.
438 : */
439 8 : value_destroy(vo_old);
440 : }
441 : }
442 : else {
443 : /* No dup found so we can insert with no check */
444 : doinsert = 1;
445 : insertmode = COL_INSERT_NOCHECK;
446 : }
447 : }
448 :
449 125 : if (doinsert) {
450 : /* Add value to collection */
451 117 : error = col_insert_binary_property(acceptor,
452 : NULL,
453 : COL_DSP_END,
454 : NULL,
455 : 0,
456 : insertmode,
457 : property,
458 : &new_vo,
459 : sizeof(struct value_obj *));
460 117 : if (error) {
461 12 : value_destroy(new_vo);
462 :
463 12 : if ((suppress) && (error == EEXIST)) {
464 : /* We are here is we do not allow dups
465 : * but found one and need to ignore it.
466 : */
467 : TRACE_INFO_STRING("Preseved exisitng value",
468 : property);
469 : error = 0;
470 : }
471 : else {
472 : /* Check if this is a critical error or not */
473 4 : if ((mergemode == INI_MV2S_ERROR) && (error == EEXIST)) {
474 : TRACE_ERROR_NUMBER("Failed to add value object to "
475 : "the section in error mode ", error);
476 4 : passed_data->error = EEXIST;
477 4 : *dummy = 1;
478 : }
479 : else {
480 : TRACE_ERROR_NUMBER("Failed to add value object"
481 : " to the section", error);
482 : return error;
483 : }
484 : }
485 : }
486 : }
487 :
488 : TRACE_FLOW_EXIT();
489 125 : return error;
490 : }
491 :
492 :
493 : /* Internal function to merge two configs */
494 28 : static int merge_two_sections(struct collection_item *donor,
495 : struct collection_item *acceptor,
496 : uint32_t flags)
497 : {
498 28 : int error = EOK;
499 : struct merge_data data;
500 :
501 : TRACE_FLOW_ENTRY();
502 :
503 28 : data.ci = acceptor;
504 28 : data.flags = flags;
505 28 : data.error = 0;
506 28 : data.found = 0;
507 :
508 28 : error = col_traverse_collection(donor,
509 : COL_TRAVERSE_ONELEVEL,
510 : merge_section_handler,
511 : (void *)(&data));
512 28 : if (error) {
513 : TRACE_ERROR_NUMBER("Merge values failed", error);
514 : return error;
515 : }
516 :
517 : TRACE_FLOW_EXIT();
518 28 : return data.error;
519 : }
520 :
521 :
522 :
523 : /* Callback to process the accepting config */
524 43 : static int acceptor_handler(const char *property,
525 : int property_len,
526 : int type,
527 : void *data,
528 : int length,
529 : void *custom_data,
530 : int *dummy)
531 : {
532 43 : int error = EOK;
533 : struct merge_data *passed_data;
534 43 : struct collection_item *acceptor = NULL;
535 43 : struct collection_item *donor = NULL;
536 : uint32_t mergemode;
537 :
538 : TRACE_FLOW_ENTRY();
539 :
540 : /* This callback is called when the dup section is found */
541 43 : passed_data = (struct merge_data *)(custom_data);
542 43 : passed_data->found = 1;
543 :
544 43 : donor = passed_data->ci;
545 43 : acceptor = *((struct collection_item **)(data));
546 :
547 43 : mergemode = passed_data->flags & INI_MS_MASK;
548 :
549 43 : switch (mergemode) {
550 : case INI_MS_ERROR: /* Report error and return */
551 : TRACE_INFO_STRING("Error ",
552 : "duplicate section");
553 5 : passed_data->error = EEXIST;
554 5 : break;
555 :
556 : case INI_MS_PRESERVE: /* Preserve what we have */
557 : TRACE_INFO_STRING("Preserve mode", "");
558 : break;
559 :
560 : case INI_MS_OVERWRITE: /* Empty existing section */
561 : TRACE_INFO_STRING("Ovewrite mode", "");
562 9 : error = empty_section(acceptor);
563 9 : if (error) {
564 : TRACE_ERROR_NUMBER("Failed to "
565 : "empty section",
566 : error);
567 : return error;
568 : }
569 9 : error = merge_two_sections(donor,
570 : acceptor,
571 : passed_data->flags);
572 9 : if (error) {
573 : TRACE_ERROR_NUMBER("Failed to merge "
574 : "sections", error);
575 3 : if (error == EEXIST) {
576 3 : passed_data->error = error;
577 : }
578 3 : return error;
579 : }
580 : break;
581 :
582 : case INI_MS_DETECT: /* Detect mode */
583 : TRACE_INFO_STRING("Detect mode", "");
584 10 : passed_data->error = EEXIST;
585 10 : error = merge_two_sections(donor,
586 : acceptor,
587 : passed_data->flags);
588 10 : if (error) {
589 4 : if (error != EEXIST) {
590 : TRACE_ERROR_NUMBER("Failed to merge "
591 : "sections", error);
592 : return error;
593 : }
594 : }
595 : break;
596 :
597 : case INI_MS_MERGE: /* Merge */
598 : default: TRACE_INFO_STRING("Merge mode", "");
599 9 : error = merge_two_sections(donor,
600 : acceptor,
601 : passed_data->flags);
602 9 : if (error) {
603 3 : if (error != EEXIST) {
604 : TRACE_ERROR_NUMBER("Failed to merge "
605 : "sections", error);
606 : return error;
607 : }
608 3 : passed_data->error = error;
609 : }
610 : break;
611 : }
612 :
613 40 : *dummy = 1;
614 : TRACE_FLOW_EXIT();
615 40 : return EOK;
616 : }
617 :
618 : /* Callback to process the donating config */
619 86 : static int donor_handler(const char *property,
620 : int property_len,
621 : int type,
622 : void *data,
623 : int length,
624 : void *custom_data,
625 : int *dummy)
626 : {
627 86 : int error = EOK;
628 : struct merge_data *passed_data;
629 : struct merge_data acceptor_data;
630 86 : struct collection_item *new_ci = NULL;
631 :
632 : TRACE_FLOW_ENTRY();
633 :
634 86 : *dummy = 0;
635 :
636 : /* Opaque data passed to callback is merge data */
637 86 : passed_data = (struct merge_data *)(custom_data);
638 :
639 : TRACE_INFO_STRING("Property: ", property);
640 : TRACE_INFO_NUMBER("Type is: ", type);
641 : TRACE_INFO_NUMBER("Flags: ", passed_data->flags);
642 :
643 : /* All sections are subcollections */
644 86 : if(type == COL_TYPE_COLLECTIONREF) {
645 :
646 : /* Prepare data for the next callback */
647 61 : acceptor_data.flags = passed_data->flags;
648 61 : acceptor_data.ci = *((struct collection_item **)(data));
649 61 : acceptor_data.error = 0;
650 61 : acceptor_data.found = 0;
651 :
652 : /* Try to find same section as the current one */
653 61 : error = col_get_item_and_do(passed_data->ci,
654 : property,
655 : COL_TYPE_COLLECTIONREF,
656 : COL_TRAVERSE_ONELEVEL,
657 : acceptor_handler,
658 : (void *)(&acceptor_data));
659 61 : if (error) {
660 : TRACE_ERROR_NUMBER("Critical error", error);
661 : return error;
662 : }
663 :
664 : /* Was duplicate found ? */
665 61 : if (acceptor_data.found) {
666 : /* Check for logical error. It can be only EEXIST */
667 43 : if (acceptor_data.error) {
668 : /* Save error anyway */
669 21 : passed_data->error = acceptor_data.error;
670 : /* If it is section DETECT or MERGE+DETECT */
671 21 : if (((passed_data->flags & INI_MS_MASK) == INI_MS_DETECT) ||
672 6 : (((passed_data->flags & INI_MS_MASK) != INI_MS_ERROR) &&
673 6 : ((passed_data->flags & INI_MV2S_MASK) ==
674 : INI_MV2S_DETECT))) {
675 : TRACE_INFO_NUMBER("Non-critical error",
676 : acceptor_data.error);
677 : }
678 : else {
679 : /* In any other mode we need to stop */
680 : TRACE_INFO_NUMBER("Merge error detected",
681 : acceptor_data.error);
682 : /* Force stop */
683 7 : *dummy = 1;
684 : }
685 : }
686 : }
687 : else {
688 : /* Not found? Then create a copy... */
689 18 : error = col_copy_collection_with_cb(&new_ci,
690 : acceptor_data.ci,
691 : NULL,
692 : COL_COPY_NORMAL,
693 : ini_copy_cb,
694 : NULL);
695 18 : if (error) {
696 : TRACE_ERROR_NUMBER("Failed to copy collection", error);
697 : return error;
698 : }
699 :
700 : /* ... and embed into the existing collection */
701 18 : error = col_add_collection_to_collection(passed_data->ci,
702 : NULL,
703 : NULL,
704 : new_ci,
705 : COL_ADD_MODE_EMBED);
706 18 : if (error) {
707 : TRACE_ERROR_NUMBER("Failed to copy collection", error);
708 0 : col_destroy_collection(new_ci);
709 0 : return error;
710 : }
711 : }
712 : }
713 :
714 : TRACE_FLOW_EXIT();
715 : return EOK;
716 : }
717 :
718 17 : static int merge_comment(struct ini_cfgobj *donor,
719 : struct ini_cfgobj *acceptor)
720 : {
721 17 : int error = EOK;
722 :
723 : TRACE_FLOW_ENTRY();
724 :
725 17 : if (donor->last_comment) {
726 :
727 17 : if (acceptor->last_comment) {
728 :
729 17 : error = ini_comment_add(donor->last_comment,
730 : acceptor->last_comment);
731 17 : if (error) {
732 : TRACE_ERROR_NUMBER("Merge comment failed", error);
733 : return error;
734 : }
735 :
736 : }
737 : else {
738 0 : error = ini_comment_copy(donor->last_comment,
739 : &(acceptor->last_comment));
740 0 : if (error) {
741 : TRACE_ERROR_NUMBER("Copy comment failed", error);
742 : return error;
743 : }
744 : }
745 : }
746 :
747 : TRACE_FLOW_EXIT();
748 : return EOK;
749 : }
750 :
751 :
752 :
753 : /* Internal function to merge two configs */
754 42 : static int merge_configs(struct ini_cfgobj *donor,
755 : struct ini_cfgobj *acceptor,
756 : uint32_t collision_flags)
757 : {
758 25 : int error = EOK;
759 : struct merge_data data;
760 :
761 : TRACE_FLOW_ENTRY();
762 :
763 25 : data.ci = acceptor->cfg;
764 25 : data.flags = collision_flags;
765 25 : data.error = 0;
766 25 : data.found = 0;
767 :
768 : /* Loop through the donor collection calling
769 : * donor_handler callback for every section we find.
770 : */
771 25 : error = col_traverse_collection(donor->cfg,
772 : COL_TRAVERSE_ONELEVEL,
773 : donor_handler,
774 : (void *)(&data));
775 25 : if (error) {
776 : TRACE_ERROR_NUMBER("Merge failed", error);
777 : return error;
778 : }
779 :
780 : /* Check if we got error */
781 39 : if ((data.error) &&
782 23 : (((collision_flags & INI_MS_MASK) == INI_MS_ERROR) ||
783 9 : ((collision_flags & INI_MV2S_MASK) == INI_MV2S_ERROR))) {
784 : TRACE_ERROR_NUMBER("Got error in error mode", data.error);
785 : return data.error;
786 : }
787 :
788 : /* If boundaries are different re-align the values */
789 17 : if (acceptor->boundary != donor->boundary) {
790 0 : error = ini_config_set_wrap(acceptor, acceptor->boundary);
791 0 : if (error) {
792 : TRACE_ERROR_NUMBER("Failed to re-align", error);
793 : return error;
794 : }
795 : }
796 :
797 : /* Merge last comment */
798 17 : error = merge_comment(donor, acceptor);
799 17 : if (error) {
800 : TRACE_ERROR_NUMBER("Failed to merge comment", error);
801 : return error;
802 : }
803 :
804 : /* Check if we got error */
805 23 : if ((data.error) &&
806 8 : (((collision_flags & INI_MS_MASK) == INI_MS_DETECT) ||
807 2 : ((collision_flags & INI_MV2S_MASK) == INI_MV2S_DETECT))) {
808 : TRACE_ERROR_NUMBER("Got error in error or detect mode", data.error);
809 6 : error = data.error;
810 : }
811 :
812 : TRACE_FLOW_EXIT();
813 17 : return error;
814 : }
815 :
816 : /* Check if collision flags are valid */
817 271 : int valid_collision_flags(uint32_t collision_flags)
818 : {
819 : uint32_t flag;
820 :
821 : TRACE_FLOW_ENTRY();
822 :
823 271 : flag = collision_flags & INI_MV1S_MASK;
824 271 : if ((flag != INI_MV1S_OVERWRITE) &&
825 : (flag != INI_MV1S_ERROR) &&
826 : (flag != INI_MV1S_PRESERVE) &&
827 : (flag != INI_MV1S_ALLOW) &&
828 : (flag != INI_MV1S_DETECT)) {
829 : TRACE_ERROR_STRING("Invalid value collision flag","");
830 : return 0;
831 : }
832 :
833 271 : flag = collision_flags & INI_MV2S_MASK;
834 542 : if ((flag != INI_MV2S_OVERWRITE) &&
835 271 : (flag != INI_MV2S_ERROR) &&
836 142 : (flag != INI_MV2S_PRESERVE) &&
837 81 : (flag != INI_MV2S_ALLOW) &&
838 : (flag != INI_MV2S_DETECT)) {
839 : TRACE_ERROR_STRING("Invalid value cross-section collision flag","");
840 : return 0;
841 : }
842 :
843 271 : flag = collision_flags & INI_MS_MASK;
844 542 : if ((flag != INI_MS_MERGE) &&
845 271 : (flag != INI_MS_OVERWRITE) &&
846 100 : (flag != INI_MS_ERROR) &&
847 70 : (flag != INI_MS_PRESERVE) &&
848 : (flag != INI_MS_DETECT)) {
849 : TRACE_ERROR_STRING("Invalid section collision flag","");
850 : return 0;
851 : }
852 :
853 : TRACE_FLOW_EXIT();
854 271 : return 1;
855 : }
856 :
857 : /* Merge two configurations together creating a new one */
858 25 : int ini_config_merge(struct ini_cfgobj *first,
859 : struct ini_cfgobj *second,
860 : uint32_t collision_flags,
861 : struct ini_cfgobj **result)
862 : {
863 25 : int error = EOK;
864 25 : struct ini_cfgobj *new_co = NULL;
865 :
866 : TRACE_FLOW_ENTRY();
867 :
868 : /* Check input params */
869 50 : if ((!first) ||
870 50 : (!second) ||
871 : (!result)) {
872 : TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
873 : return EINVAL;
874 : }
875 :
876 : /* Check collision flags */
877 25 : if (!valid_collision_flags(collision_flags)) {
878 : TRACE_ERROR_NUMBER("Invalid flags.", EINVAL);
879 : return EINVAL;
880 : }
881 :
882 : /* NOTE: We assume that the configuration we merge to
883 : * is consistent regarding duplicate values.
884 : * For example, if the duplicates are not allowed,
885 : * the parsing function should have been instructed
886 : * to not allow duplicates.
887 : * If in future we decide to be explicite we would need
888 : * to introduce a "compacting" function and call it here
889 : * after we create a copy.
890 : * For now it is treated as a corner case and thus not worth
891 : * implementing.
892 : */
893 :
894 : /* Create a new config object */
895 25 : error = ini_config_copy(first, &new_co);
896 25 : if (error) {
897 : TRACE_ERROR_NUMBER("Failed to copy configuration", error);
898 : return error;
899 : }
900 :
901 : /* Merge configs */
902 25 : error = merge_configs(second, new_co, collision_flags);
903 25 : if (error) {
904 : TRACE_ERROR_NUMBER("Failed to merge configuration", error);
905 28 : if ((error == EEXIST) &&
906 19 : ((((collision_flags & INI_MS_MASK) == INI_MS_DETECT) &&
907 15 : ((collision_flags & INI_MV2S_MASK) != INI_MV2S_ERROR)) ||
908 5 : (((collision_flags & INI_MS_MASK) != INI_MS_ERROR) &&
909 5 : ((collision_flags & INI_MV2S_MASK) == INI_MV2S_DETECT)))) {
910 : TRACE_ERROR_NUMBER("Got error in detect mode", error);
911 : /* Fall through! */
912 : }
913 : else {
914 : /* Got an error in any other mode */
915 : TRACE_ERROR_NUMBER("Got error in non detect mode", error);
916 8 : ini_config_destroy(new_co);
917 8 : return error;
918 : }
919 : }
920 :
921 17 : *result = new_co;
922 : TRACE_FLOW_EXIT();
923 17 : return error;
924 :
925 : }
926 :
927 : /* How many errors do we have in the list ? */
928 32 : unsigned ini_config_error_count(struct ini_cfgobj *cfg_ctx)
929 : {
930 32 : unsigned count = 0;
931 :
932 : TRACE_FLOW_ENTRY();
933 :
934 32 : count = cfg_ctx->count;
935 :
936 : TRACE_FLOW_EXIT();
937 32 : return count;
938 :
939 : }
940 :
941 : /* Free error strings */
942 26 : void ini_config_free_errors(char **errors)
943 : {
944 : TRACE_FLOW_ENTRY();
945 :
946 26 : col_free_property_list(errors);
947 :
948 : TRACE_FLOW_EXIT();
949 26 : }
950 :
951 : /* Get the list of error strings */
952 26 : int ini_config_get_errors(struct ini_cfgobj *cfg_ctx,
953 : char ***errors)
954 : {
955 26 : char **errlist = NULL;
956 26 : struct collection_iterator *iterator = NULL;
957 : int error;
958 26 : struct collection_item *item = NULL;
959 : struct ini_parse_error *pe;
960 26 : unsigned int count = 0;
961 : char *line;
962 :
963 : TRACE_FLOW_ENTRY();
964 :
965 : /* If we have something to print print it */
966 26 : if ((!errors) || (!cfg_ctx)) {
967 : TRACE_ERROR_NUMBER("Invalid parameter.", EINVAL);
968 : return EINVAL;
969 : }
970 :
971 26 : errlist = calloc(cfg_ctx->count + 1, sizeof(char *));
972 26 : if (!errlist) {
973 : TRACE_ERROR_NUMBER("Failed to allocate memory for errors.", ENOMEM);
974 : return ENOMEM;
975 : }
976 :
977 : /* Bind iterator */
978 26 : error = col_bind_iterator(&iterator,
979 : cfg_ctx->error_list,
980 : COL_TRAVERSE_DEFAULT);
981 26 : if (error) {
982 : TRACE_ERROR_NUMBER("Faile to bind iterator:", error);
983 0 : ini_config_free_errors(errlist);
984 0 : return error;
985 : }
986 :
987 : while(1) {
988 : /* Loop through a collection */
989 143 : error = col_iterate_collection(iterator, &item);
990 143 : if (error) {
991 : TRACE_ERROR_NUMBER("Error iterating collection", error);
992 0 : col_unbind_iterator(iterator);
993 0 : ini_config_free_errors(errlist);
994 0 : return error;
995 : }
996 :
997 : /* Are we done ? */
998 143 : if (item == NULL) break;
999 :
1000 : /* Process collection header */
1001 117 : if (col_get_item_type(item) == COL_TYPE_COLLECTION) {
1002 26 : continue;
1003 : }
1004 : else {
1005 : /* Put error into provided format */
1006 91 : pe = (struct ini_parse_error *)(col_get_item_data(item));
1007 :
1008 : /* Would be nice to have asprintf function...
1009 : * ...but for now we know that all the errors
1010 : * are pretty short and will fir into the predefined
1011 : * error length buffer.
1012 : */
1013 91 : line = malloc(MAX_ERROR_LINE + 1);
1014 91 : if (!line) {
1015 : TRACE_ERROR_NUMBER("Failed to get memory for error.", ENOMEM);
1016 0 : col_unbind_iterator(iterator);
1017 0 : ini_config_free_errors(errlist);
1018 0 : return ENOMEM;
1019 : }
1020 :
1021 91 : snprintf(line, MAX_ERROR_LINE, LINE_FORMAT,
1022 : col_get_item_property(item, NULL),
1023 : pe->error,
1024 : pe->line,
1025 : ini_get_error_str(pe->error,
1026 : INI_FAMILY_PARSING));
1027 :
1028 91 : errlist[count] = line;
1029 91 : count++;
1030 : }
1031 :
1032 : }
1033 :
1034 : /* Do not forget to unbind iterator - otherwise there will be a leak */
1035 26 : col_unbind_iterator(iterator);
1036 :
1037 26 : *errors = errlist;
1038 :
1039 : TRACE_FLOW_EXIT();
1040 26 : return error;
1041 : }
|