1 /*
2 INI LIBRARY
3
4 Low level parsing functions
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 <string.h>
25 #include <ctype.h>
26 /* For error text */
27 #include <libintl.h>
28 #define _(String) gettext (String)
29 #include "trace.h"
30 #include "ini_defines.h"
31 #include "ini_valueobj.h"
32 #include "ini_configobj.h"
33 #include "ini_config_priv.h"
34 #include "collection.h"
35 #include "collection_queue.h"
36
37 #define INI_WARNING 0xA0000000 /* Warning bit */
38
39
40 struct parser_obj {
41 /* Externally passed and saved data */
42 FILE *file;
43 struct collection_item *top;
44 struct collection_item *el;
45 const char *filename;
46 struct ini_cfgobj *co;
47 /* Level of error reporting */
48 int error_level;
49 /* Collistion flags */
50 uint32_t collision_flags;
51 /* Parseing flags */
52 uint32_t parse_flags;
53 /* Wrapping boundary */
54 uint32_t boundary;
55 /* Action queue */
56 struct collection_item *queue;
57 /* Last error */
58 uint32_t last_error;
59 /* Last line number */
60 uint32_t linenum;
61 /* Line number of the last found key */
62 uint32_t keylinenum;
63 /* Line number of the last found section */
64 uint32_t seclinenum;
65 /* Internal variables */
66 struct collection_item *sec;
67 struct collection_item *merge_sec;
68 struct ini_comment *ic;
69 char *last_read;
70 uint32_t last_read_len;
71 int inside_comment;
72 char *key;
73 uint32_t key_len;
74 struct ref_array *raw_lines;
75 struct ref_array *raw_lengths;
76 char *merge_key;
77 struct value_obj *merge_vo;
78 /* Merge error */
79 uint32_t merge_error;
80 int ret;
81 };
82
83 typedef int (*action_fn)(struct parser_obj *);
84
85 #define PARSE_ACTION "action"
86
87 /* Actions */
88 #define PARSE_READ 0 /* Read from the file */
89 #define PARSE_INSPECT 1 /* Process read string */
90 #define PARSE_POST 2 /* Reading is complete */
91 #define PARSE_ERROR 3 /* Handle error */
92 #define PARSE_DONE 4 /* We are done */
93
94 /* Declarations of the reusble functions: */
95 static int complete_value_processing(struct parser_obj *po);
96 static int save_error(struct collection_item *el,
97 unsigned line,
98 int error,
99 const char *err_txt);
100
101
is_just_spaces(const char * str,uint32_t len)102 static int is_just_spaces(const char *str, uint32_t len)
103 {
104 uint32_t i;
105
106 TRACE_FLOW_ENTRY();
107
108 for (i = 0; i < len; i++) {
109 if (!isspace(str[i])) return 0;
110 }
111
112 TRACE_FLOW_EXIT();
113 return 1;
114 }
115
116 /* Functions checks whether the line
117 * starts with the sequence of allowed blank characters.
118 * If spaces are allowed - function will say that line
119 * is OK. If tabls are allowed the function also would
120 * say that line is OK. If the mixture of both is allowed
121 * the line is OK too.
122 * Any other character will cause an error.
123 */
is_allowed_spaces(const char * str,uint32_t len,uint32_t parse_flags,int * error)124 static int is_allowed_spaces(const char *str,
125 uint32_t len,
126 uint32_t parse_flags,
127 int *error)
128 {
129 uint32_t i;
130 int line_ok = 1;
131
132 TRACE_FLOW_ENTRY();
133
134 for (i = 0; i < len; i++) {
135 if ((str[i] == ' ') &&
136 (parse_flags & INI_PARSE_NOSPACE)) {
137 /* Leading spaces are not allowed */
138 *error = ERR_SPACE;
139 line_ok = 0;
140 break;
141 }
142 else if ((str[i] == '\t') &&
143 (parse_flags & INI_PARSE_NOTAB)) {
144 /* Leading tabs are not allowed */
145 *error = ERR_TAB;
146 line_ok = 0;
147 break;
148 }
149 else if ((str[i] == '\f') ||
150 (str[i] == '\n') ||
151 (str[i] == '\r') ||
152 (str[i] == '\v')) {
153 *error = ERR_SPECIAL;
154 line_ok = 0;
155 break;
156 }
157 if (!isblank(str[i])) break;
158 }
159
160 TRACE_FLOW_EXIT();
161 return line_ok;
162 }
163
164 /* Destroy parser object */
parser_destroy(struct parser_obj * po)165 static void parser_destroy(struct parser_obj *po)
166 {
167 TRACE_FLOW_ENTRY();
168
169 if(po) {
170 col_destroy_queue(po->queue);
171 col_destroy_collection_with_cb(po->sec, ini_cleanup_cb, NULL);
172 ini_comment_destroy(po->ic);
173 value_destroy_arrays(po->raw_lines,
174 po->raw_lengths);
175 if (po->last_read) free(po->last_read);
176 if (po->key) free(po->key);
177 col_destroy_collection_with_cb(po->top, ini_cleanup_cb, NULL);
178 free(po);
179 }
180
181 TRACE_FLOW_EXIT();
182 }
183
184 /* Create parse object
185 *
186 * It assumes that the ini collection
187 * has been precreated.
188 */
parser_create(struct ini_cfgobj * co,FILE * file,const char * config_filename,int error_level,uint32_t collision_flags,uint32_t parse_flags,struct parser_obj ** po)189 static int parser_create(struct ini_cfgobj *co,
190 FILE *file,
191 const char *config_filename,
192 int error_level,
193 uint32_t collision_flags,
194 uint32_t parse_flags,
195 struct parser_obj **po)
196 {
197 int error = EOK;
198 struct parser_obj *new_po = NULL;
199 unsigned count = 0;
200
201 TRACE_FLOW_ENTRY();
202
203 /* Make sure that all the parts are initialized */
204 if ((!po) ||
205 (!co) ||
206 (!(co->cfg)) ||
207 (!file) ||
208 (!config_filename)) {
209 TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
210 return EINVAL;
211 }
212
213 error = col_get_collection_count(co->cfg, &count);
214 if (error) {
215 TRACE_ERROR_NUMBER("Failed to check object size", error);
216 return error;
217 }
218
219 if (count != 1) {
220 TRACE_ERROR_NUMBER("Configuration is not empty", EINVAL);
221 return EINVAL;
222 }
223
224 new_po = malloc(sizeof(struct parser_obj));
225 if (!new_po) {
226 TRACE_ERROR_NUMBER("No memory", ENOMEM);
227 return ENOMEM;
228 }
229
230 /* Save external data */
231 new_po->file = file;
232 new_po->el = co->error_list;
233 new_po->filename = config_filename;
234 new_po->error_level = error_level;
235 new_po->collision_flags = collision_flags;
236 new_po->parse_flags = parse_flags;
237 new_po->boundary = co->boundary;
238 new_po->co = co;
239
240 /* Initialize internal varibles */
241 new_po->sec = NULL;
242 new_po->merge_sec = NULL;
243 new_po->ic = NULL;
244 new_po->last_error = 0;
245 new_po->linenum = 0;
246 new_po->keylinenum = 0;
247 new_po->seclinenum = 0;
248 new_po->last_read = NULL;
249 new_po->last_read_len = 0;
250 new_po->inside_comment = 0;
251 new_po->key = NULL;
252 new_po->key_len = 0;
253 new_po->raw_lines = NULL;
254 new_po->raw_lengths = NULL;
255 new_po->ret = EOK;
256 new_po->merge_key = NULL;
257 new_po->merge_vo = NULL;
258 new_po->merge_error = 0;
259 new_po->top = NULL;
260 new_po->queue = NULL;
261
262 /* Create top collection */
263 error = col_create_collection(&(new_po->top),
264 INI_CONFIG_NAME,
265 COL_CLASS_INI_CONFIG);
266 if (error) {
267 TRACE_ERROR_NUMBER("Failed to create top collection", error);
268 parser_destroy(new_po);
269 return error;
270 }
271
272 /* Create a queue */
273 error = col_create_queue(&(new_po->queue));
274 if (error) {
275 TRACE_ERROR_NUMBER("Failed to create queue", error);
276 parser_destroy(new_po);
277 return error;
278 }
279
280 error = col_enqueue_unsigned_property(new_po->queue,
281 PARSE_ACTION,
282 PARSE_READ);
283 if (error) {
284 TRACE_ERROR_NUMBER("Failed to create queue", error);
285 parser_destroy(new_po);
286 return error;
287 }
288
289 *po = new_po;
290
291 TRACE_FLOW_EXIT();
292 return error;
293 }
294
295 /* Function to read next line from the file */
parser_read(struct parser_obj * po)296 static int parser_read(struct parser_obj *po)
297 {
298 int error = EOK;
299 char *buffer = NULL;
300 ssize_t res = 0;
301 size_t len = 0;
302 int32_t i = 0;
303 uint32_t action;
304
305 TRACE_FLOW_ENTRY();
306
307 /* Adjust line number */
308 (po->linenum)++;
309
310 /* Get line from the file */
311 res = getline(&buffer, &len, po->file);
312 if (res == -1) {
313 if (feof(po->file)) {
314 TRACE_FLOW_STRING("Read nothing", "");
315 if (po->inside_comment) {
316 action = PARSE_ERROR;
317 po->last_error = ERR_BADCOMMENT;
318 }
319 else action = PARSE_POST;
320 }
321 else {
322 TRACE_ERROR_STRING("Error reading", "");
323 action = PARSE_ERROR;
324 po->last_error = ERR_READ;
325 }
326 if(buffer) free(buffer);
327 }
328 else {
329 /* Read Ok */
330 len = res;
331 TRACE_INFO_STRING("Read line ok:", buffer);
332 TRACE_INFO_NUMBER("Length:", len);
333 TRACE_INFO_NUMBER("Strlen:", strlen(buffer));
334
335 if (buffer[0] == '\0') {
336 /* Empty line - read again (should not ever happen) */
337 action = PARSE_READ;
338 free(buffer);
339 }
340 else {
341 /* Trim end line */
342 i = len - 1;
343 while ((i >= 0) &&
344 ((buffer[i] == '\r') ||
345 (buffer[i] == '\n'))) {
346 TRACE_INFO_NUMBER("Offset:", i);
347 TRACE_INFO_NUMBER("Code:", buffer[i]);
348 buffer[i] = '\0';
349 i--;
350 }
351
352 po->last_read = buffer;
353 po->last_read_len = i + 1;
354 action = PARSE_INSPECT;
355 TRACE_INFO_STRING("Line:", po->last_read);
356 TRACE_INFO_NUMBER("Linelen:", po->last_read_len);
357 }
358 }
359
360 /* Move to the next action */
361 error = col_enqueue_unsigned_property(po->queue,
362 PARSE_ACTION,
363 action);
364 if (error) {
365 TRACE_ERROR_NUMBER("Failed to schedule an action", error);
366 return error;
367 }
368
369 TRACE_FLOW_EXIT();
370 return EOK;
371 }
372
373 /* Find if there is a collistion */
check_section_collision(struct parser_obj * po)374 static int check_section_collision(struct parser_obj *po)
375 {
376 int error = EOK;
377 struct collection_item *item = NULL;
378
379 TRACE_FLOW_ENTRY();
380
381 TRACE_INFO_STRING("Searching for:", col_get_item_property(po->sec, NULL));
382
383 error = col_get_item(po->top,
384 col_get_item_property(po->sec, NULL),
385 COL_TYPE_COLLECTIONREF,
386 COL_TRAVERSE_DEFAULT,
387 &item);
388
389 if (error) {
390 TRACE_ERROR_NUMBER("Failed searching for dup", error);
391 return error;
392 }
393
394 /* Check if there is a dup */
395 if (item) {
396 TRACE_INFO_STRING("Collision found:",
397 col_get_item_property(item, NULL));
398 /* Get the actual section collection instead of reference */
399 po->merge_sec = *((struct collection_item **)
400 (col_get_item_data(item)));
401 }
402 else {
403 TRACE_INFO_STRING("Collision not found.", "");
404 po->merge_sec = NULL;
405 }
406
407 TRACE_FLOW_EXIT();
408 return EOK;
409 }
410
411 /* Clean all items in the section */
empty_section(struct collection_item * sec)412 int empty_section(struct collection_item *sec)
413 {
414 int error = EOK;
415 struct collection_item *item = NULL;
416 struct collection_item *save_item = NULL;
417 struct value_obj *vo = NULL;
418 int work_to_do = 1;
419
420 TRACE_FLOW_ENTRY();
421
422 do {
423 item = NULL;
424 error = col_extract_item_from_current(sec,
425 COL_DSP_FRONT,
426 NULL,
427 0,
428 COL_TYPE_ANY,
429 &item);
430 if ((error) && (error != ENOENT)) {
431 TRACE_ERROR_NUMBER("Failed to extract item.", error);
432 return error;
433 }
434
435 if (item) {
436 TRACE_INFO_STRING("Item found:",
437 col_get_item_property(item, NULL));
438
439 if (strncmp(col_get_item_property(item, NULL),
440 INI_SECTION_KEY, 1) == 0) {
441 /* Just ignore the first item */
442 save_item = item;
443 continue;
444 }
445
446 vo = *((struct value_obj **)(col_get_item_data(item)));
447 value_destroy(vo);
448 col_delete_item(item);
449 }
450 else {
451 TRACE_INFO_STRING("No more items:", "");
452 /* Restore saved item */
453 error = col_insert_item(sec,
454 NULL,
455 save_item,
456 COL_DSP_END,
457 NULL,
458 0,
459 COL_INSERT_NOCHECK);
460 if (error) {
461 TRACE_ERROR_NUMBER("Failed to restore item.", error);
462 return error;
463 }
464
465 work_to_do = 0;
466 }
467 }
468 while (work_to_do);
469
470 TRACE_FLOW_EXIT();
471 return EOK;
472 }
473
474 /* Merge contents of the section */
merge_section(struct parser_obj * po)475 static int merge_section(struct parser_obj *po)
476 {
477 int error = EOK;
478 struct collection_item *item = NULL;
479 struct value_obj *vo = NULL;
480 int work_to_do = 1;
481 const char *key;
482
483 TRACE_FLOW_ENTRY();
484
485 do {
486 TRACE_INFO_STRING("Top of the merge loop", "");
487
488 item = NULL;
489 error = col_extract_item_from_current(po->sec,
490 COL_DSP_FRONT,
491 NULL,
492 0,
493 COL_TYPE_ANY,
494 &item);
495 if ((error) && (error != ENOENT)) {
496 TRACE_ERROR_NUMBER("Failed to extract item.", error);
497 return error;
498 }
499
500 if (item) {
501
502 TRACE_INFO_STRING("Item found:", col_get_item_property(item, NULL));
503
504 if (strncmp(col_get_item_property(item, NULL),
505 INI_SECTION_KEY, 1) == 0) {
506 /* Just ignore the first item */
507 vo = *((struct value_obj **)(col_get_item_data(item)));
508 value_destroy(vo);
509 col_delete_item(item);
510 continue;
511 }
512
513 po->merge_vo = *((struct value_obj **)(col_get_item_data(item)));
514 key = col_get_item_property(item, NULL);
515 /* To be able to use po->merge_key in the loop
516 * we have to overcome constraints imposed by
517 * the "const" declaration.
518 */
519 memcpy(&(po->merge_key), &key, sizeof(char *));
520
521 /* Use the value processing function to inser the value */
522 error = complete_value_processing(po);
523
524 /* In case of error value is already cleaned */
525 po->merge_vo = NULL;
526 po->merge_key = NULL;
527 col_delete_item(item);
528 /* Now we can check the error */
529 if (error) {
530 TRACE_ERROR_NUMBER("Failed to merge item.", error);
531 return error;
532 }
533 }
534 else {
535 TRACE_INFO_STRING("No more items:", "");
536 work_to_do = 0;
537 }
538 }
539 while (work_to_do);
540
541 /* If we reached this place the incoming section is empty.
542 * but just to be safe clean with callback. */
543 col_destroy_collection_with_cb(po->sec, ini_cleanup_cb, NULL);
544 po->sec = NULL;
545
546 TRACE_FLOW_EXIT();
547 return EOK;
548 }
549
550 /* Function to read next line from the file */
parser_save_section(struct parser_obj * po)551 static int parser_save_section(struct parser_obj *po)
552 {
553 int error = EOK;
554 uint32_t mergemode;
555 int merge = 0;
556
557 TRACE_FLOW_ENTRY();
558
559 if (po->sec) {
560
561 TRACE_INFO_STRING("Section exists.", "");
562
563 /* First detect if we have collision */
564 error = check_section_collision(po);
565 if (error) {
566 TRACE_ERROR_NUMBER("Failed to check for collision", error);
567 return error;
568 }
569
570 if (po->merge_sec) {
571
572 TRACE_INFO_STRING("Merge collision detected", "");
573
574 mergemode = po->collision_flags & INI_MS_MODE_MASK;
575
576 switch (mergemode) {
577 case INI_MS_ERROR:
578 /* Report error and return */
579 TRACE_INFO_STRING("Reporting error", "duplicate section");
580 error = save_error(po->el,
581 po->seclinenum,
582 ERR_DUPSECTION,
583 ERROR_TXT);
584 if (error) {
585 TRACE_ERROR_NUMBER("Failed to "
586 "save error",
587 error);
588 return error;
589 }
590 /* Return error */
591 TRACE_FLOW_RETURN(EEXIST);
592 return EEXIST;
593
594 case INI_MS_PRESERVE:
595 /* Delete new section */
596 TRACE_INFO_STRING("Preserve mode", "");
597 col_destroy_collection_with_cb(
598 po->sec,
599 ini_cleanup_cb,
600 NULL);
601 po->sec = NULL;
602 break;
603
604 case INI_MS_OVERWRITE:
605 /* Empty existing section */
606 TRACE_INFO_STRING("Ovewrite mode", "");
607 error = empty_section(po->merge_sec);
608 if (error) {
609 TRACE_ERROR_NUMBER("Failed to "
610 "empty section",
611 error);
612 return error;
613 }
614 merge = 1;
615 break;
616
617 case INI_MS_MERGE:
618 /* Merge */
619 default:
620 TRACE_INFO_STRING("Merge mode", "");
621 merge = 1;
622 break;
623 }
624
625 if (po->collision_flags & INI_MS_DETECT) {
626 po->merge_error = EEXIST;
627 error = save_error(po->el,
628 po->seclinenum,
629 ERR_DUPSECTION,
630 ERROR_TXT);
631 if (error) {
632 TRACE_ERROR_NUMBER("Failed to "
633 "save error",
634 error);
635 return error;
636 }
637 }
638
639 if (merge) {
640 error = merge_section(po);
641 if (error) {
642 TRACE_ERROR_NUMBER("Failed to merge section", error);
643 return error;
644 }
645 }
646
647 po->merge_sec = NULL;
648 }
649 else {
650 /* Add section to configuration */
651 TRACE_INFO_STRING("Now adding collection", "");
652 error = col_add_collection_to_collection(po->top,
653 NULL, NULL,
654 po->sec,
655 COL_ADD_MODE_EMBED);
656
657 if (error) {
658 TRACE_ERROR_NUMBER("Failed to embed section", error);
659 return error;
660 }
661
662 po->sec = NULL;
663 }
664 }
665
666 TRACE_FLOW_EXIT();
667 return EOK;
668
669 }
670
671 /* Complete value processing */
complete_value_processing(struct parser_obj * po)672 static int complete_value_processing(struct parser_obj *po)
673 {
674 int error = EOK;
675 int error2 = EOK;
676 struct value_obj *vo = NULL;
677 struct value_obj *vo_old = NULL;
678 unsigned insertmode;
679 uint32_t mergemode;
680 int suppress = 0;
681 int doinsert = 0;
682 struct collection_item *item = NULL;
683 struct collection_item *section = NULL;
684 int merging = 0;
685
686 TRACE_FLOW_ENTRY();
687
688 if (po->merge_sec) {
689 TRACE_INFO_STRING("Processing value in merge mode", "");
690 section = po->merge_sec;
691 merging = 1;
692 }
693 else if(!(po->sec)) {
694 TRACE_INFO_STRING("Creating default section", "");
695 /* If there is not open section create a default one */
696 error = col_create_collection(&po->sec,
697 INI_DEFAULT_SECTION,
698 COL_CLASS_INI_SECTION);
699 if (error) {
700 TRACE_ERROR_NUMBER("Failed to create default section", error);
701 return error;
702 }
703 section = po->sec;
704 }
705 else {
706 TRACE_INFO_STRING("Processing value in normal mode", "");
707 section = po->sec;
708 }
709
710 if (merging) {
711 TRACE_INFO_STRING("Using merge key:", po->merge_key);
712 vo = po->merge_vo;
713 /* We are adding to the merge section so use MV2S flags.
714 * But flags are done in such a way that deviding MV2S by MV1S mask
715 * will translate MV2S flags into MV1S so we can use
716 * MV1S constants. */
717 TRACE_INFO_NUMBER("Collisions flags:", po->collision_flags);
718 mergemode = (po->collision_flags & INI_MV2S_MASK) / INI_MV1S_MASK;
719 }
720 else {
721 /* Construct value object from what we have */
722 error = value_create_from_refarray(po->raw_lines,
723 po->raw_lengths,
724 po->keylinenum,
725 INI_VALUE_READ,
726 po->key_len,
727 po->boundary,
728 po->ic,
729 &vo);
730
731 if (error) {
732 TRACE_ERROR_NUMBER("Failed to create value object", error);
733 return error;
734 }
735 /* Forget about the arrays. They are now owned by the value object */
736 po->ic = NULL;
737 po->raw_lines = NULL;
738 po->raw_lengths = NULL;
739 mergemode = po->collision_flags & INI_MV1S_MASK;
740 }
741
742 switch (mergemode) {
743 case INI_MV1S_ERROR:
744
745 insertmode = COL_INSERT_DUPERROR;
746 doinsert = 1;
747 break;
748
749 case INI_MV1S_PRESERVE:
750
751 insertmode = COL_INSERT_DUPERROR;
752 doinsert = 1;
753 suppress = 1;
754 break;
755
756 case INI_MV1S_ALLOW:
757
758 insertmode = COL_INSERT_NOCHECK;
759 doinsert = 1;
760 break;
761
762 case INI_MV1S_OVERWRITE: /* Special handling */
763 case INI_MV1S_DETECT:
764 default:
765 break;
766 }
767
768 /* Do not insert but search for dups first */
769 if (!doinsert) {
770 TRACE_INFO_STRING("Overwrite mode. Looking for:",
771 (char *)(merging ? po->merge_key : po->key));
772
773 error = col_get_item(section,
774 merging ? po->merge_key : po->key,
775 COL_TYPE_BINARY,
776 COL_TRAVERSE_DEFAULT,
777 &item);
778
779 if (error) {
780 TRACE_ERROR_NUMBER("Failed searching for dup", error);
781 value_destroy(vo);
782 return error;
783 }
784
785 /* Check if there is a dup */
786 if (item) {
787 /* Check if we are in the detect mode */
788 if (mergemode == INI_MV1S_DETECT) {
789 po->merge_error = EEXIST;
790 /* There is a dup - inform user about it and continue */
791 error = save_error(po->el,
792 merging ? po->seclinenum : po->keylinenum,
793 merging ? ERR_DUPKEYSEC : ERR_DUPKEY,
794 ERROR_TXT);
795 if (error) {
796 TRACE_ERROR_NUMBER("Failed to save error", error);
797 value_destroy(vo);
798 return error;
799 }
800 doinsert = 1;
801 insertmode = COL_INSERT_NOCHECK;
802
803 }
804 else {
805
806 /* Dup exists - update it */
807 vo_old = *((struct value_obj **)(col_get_item_data(item)));
808 error = col_modify_binary_item(item,
809 NULL,
810 &vo,
811 sizeof(struct value_obj *));
812 if (error) {
813 TRACE_ERROR_NUMBER("Failed updating the value", error);
814 value_destroy(vo);
815 return error;
816 }
817
818 /* If we failed to update it is better to leak then crash,
819 * so destroy original value only on the successful update.
820 */
821 value_destroy(vo_old);
822 }
823 }
824 else {
825 /* No dup found so we can insert with no check */
826 doinsert = 1;
827 insertmode = COL_INSERT_NOCHECK;
828 }
829 }
830
831 if (doinsert) {
832 /* Add value to collection */
833 error = col_insert_binary_property(section,
834 NULL,
835 COL_DSP_END,
836 NULL,
837 0,
838 insertmode,
839 merging ? po->merge_key : po->key,
840 &vo,
841 sizeof(struct value_obj *));
842 if (error) {
843 value_destroy(vo);
844
845 if ((suppress) && (error == EEXIST)) {
846 TRACE_INFO_STRING("Preseved exisitng value",
847 (char *)(merging ? po->merge_key : po->key));
848 }
849 else {
850 /* Check if this is a critical error or not */
851 if ((mergemode == INI_MV1S_ERROR) && (error == EEXIST)) {
852 TRACE_ERROR_NUMBER("Failed to add value object "
853 "to the section", error);
854 error2 = save_error(po->el,
855 merging ? po->seclinenum : po->keylinenum,
856 merging ? ERR_DUPKEYSEC : ERR_DUPKEY,
857 ERROR_TXT);
858 if (error2) {
859 TRACE_ERROR_NUMBER("Failed to save error", error2);
860 return error2;
861 }
862 return error;
863 }
864 else {
865 TRACE_ERROR_NUMBER("Failed to add value object"
866 " to the section", error);
867 return error;
868 }
869 }
870 }
871 }
872
873 if (!merging) {
874 free(po->key);
875 po->key = NULL;
876 po->key_len = 0;
877 }
878
879 TRACE_FLOW_EXIT();
880 return EOK;
881 }
882
883
884 /* Process comment */
handle_comment(struct parser_obj * po,uint32_t * action)885 static int handle_comment(struct parser_obj *po, uint32_t *action)
886 {
887 int error = EOK;
888
889 TRACE_FLOW_ENTRY();
890
891 /* We got a comment */
892 if (po->key) {
893 /* Previous value if any is complete */
894 error = complete_value_processing(po);
895 if (error) {
896 TRACE_ERROR_NUMBER("Failed to finish saving value", error);
897 return error;
898 }
899 }
900
901 if (!(po->ic)) {
902 /* Create a new comment */
903 error = ini_comment_create(&(po->ic));
904 if (error) {
905 TRACE_ERROR_NUMBER("Failed to create comment", error);
906 return error;
907 }
908 }
909
910 /* Add line to comment */
911 error = ini_comment_build_wl(po->ic,
912 po->last_read,
913 po->last_read_len);
914 if (error) {
915 TRACE_ERROR_NUMBER("Failed to add line to comment", error);
916 return error;
917 }
918 /*
919 * We are done with the comment line.
920 * Free it since comment keeps a copy.
921 */
922 free(po->last_read);
923 po->last_read = NULL;
924 po->last_read_len = 0;
925 *action = PARSE_READ;
926
927 TRACE_FLOW_EXIT();
928 return EOK;
929 }
930
931 /* Handle key-value pair */
handle_kvp(struct parser_obj * po,uint32_t * action)932 static int handle_kvp(struct parser_obj *po, uint32_t *action)
933 {
934 int error = EOK;
935 char *eq = NULL;
936 uint32_t len = 0;
937 char *dupval = NULL;
938 char *str;
939 uint32_t full_len;
940
941 TRACE_FLOW_ENTRY();
942
943 str = po->last_read;
944 full_len = po->last_read_len;
945
946 TRACE_INFO_STRING("Last read:", str);
947
948 /* Trim spaces at the beginning */
949 while ((full_len > 0) && (isspace(*(str)))) {
950 str++;
951 full_len--;
952 }
953
954 /* Check if we have the key */
955 if (*(str) == '=') {
956 TRACE_ERROR_STRING("No key", str);
957
958 if (po->parse_flags & INI_PARSE_IGNORE_NON_KVP) {
959 /* Clean everything as if nothing happened */
960 free(po->last_read);
961 po->last_read = NULL;
962 po->last_read_len = 0;
963 *action = PARSE_READ;
964 } else {
965 po->last_error = ERR_NOKEY;
966 *action = PARSE_ERROR;
967 }
968
969 TRACE_FLOW_EXIT();
970 return EOK;
971 }
972
973 /* Find "=" */
974 eq = strchr(str, '=');
975 if (eq == NULL) {
976 if (po->parse_flags & INI_PARSE_IGNORE_NON_KVP) {
977 /* Clean everything as if nothing happened */
978 free(po->last_read);
979 po->last_read = NULL;
980 po->last_read_len = 0;
981 *action = PARSE_READ;
982 } else {
983 TRACE_ERROR_STRING("No equal sign", str);
984 po->last_error = ERR_NOEQUAL;
985 *action = PARSE_ERROR;
986 }
987
988 TRACE_FLOW_EXIT();
989 return EOK;
990 }
991
992 /* Strip spaces around "=" */
993 /* Since eq > str we can substract 1 */
994 len = eq - str - 1;
995 while ((len > 0) && (isspace(*(str + len)))) len--;
996 /* Adjust length properly */
997 len++;
998
999 /* Check the key length */
1000 if(len >= MAX_KEY) {
1001 TRACE_ERROR_STRING("Key name is too long", str);
1002 po->last_error = ERR_LONGKEY;
1003 *action = PARSE_ERROR;
1004 TRACE_FLOW_EXIT();
1005 return EOK;
1006 }
1007
1008 if (po->key) {
1009 /* Complete processing of the previous value */
1010 error = complete_value_processing(po);
1011 if (error) {
1012 TRACE_ERROR_NUMBER("Failed to complete value processing", error);
1013 TRACE_FLOW_EXIT();
1014 return error;
1015 }
1016 }
1017
1018 /* Dup the key name */
1019 po->key = malloc(len + 1);
1020 if (!(po->key)) {
1021 TRACE_ERROR_NUMBER("Failed to dup key", ENOMEM);
1022 TRACE_FLOW_EXIT();
1023 return ENOMEM;
1024 }
1025
1026 memcpy(po->key, str, len);
1027 *(po->key + len) = '\0';
1028 po->key_len = len;
1029
1030 TRACE_INFO_STRING("Key:", po->key);
1031 TRACE_INFO_NUMBER("Keylen:", po->key_len);
1032
1033 len = full_len - (eq - str) - 1;
1034
1035 /* Trim spaces after equal sign */
1036 eq++;
1037 while (isspace(*eq)) {
1038 eq++;
1039 len--;
1040 }
1041
1042 TRACE_INFO_STRING("VALUE:", eq);
1043 TRACE_INFO_NUMBER("LENGTH:", len);
1044
1045 /* Dup the part of the value */
1046 dupval = malloc(len + 1);
1047 if (!dupval) {
1048 TRACE_ERROR_NUMBER("Failed to dup value", ENOMEM);
1049 TRACE_FLOW_EXIT();
1050 return ENOMEM;
1051 }
1052
1053 memcpy(dupval, eq, len);
1054 *(dupval + len) = '\0';
1055
1056 /* Create new arrays */
1057 error = value_create_arrays(&(po->raw_lines),
1058 &(po->raw_lengths));
1059 if (error) {
1060 TRACE_ERROR_NUMBER("Failed to create arrays", error);
1061 free(dupval);
1062 TRACE_FLOW_EXIT();
1063 return error;
1064 }
1065
1066 /* Save a duplicated part in the value */
1067 error = value_add_to_arrays(dupval,
1068 len,
1069 po->raw_lines,
1070 po->raw_lengths);
1071
1072 if (error) {
1073 TRACE_ERROR_NUMBER("Failed to add value to arrays", error);
1074 free(dupval);
1075 TRACE_FLOW_EXIT();
1076 return error;
1077 }
1078
1079 /* Save the line number of the last found key */
1080 po->keylinenum = po->linenum;
1081
1082 /* Prepare for reading */
1083 free(po->last_read);
1084 po->last_read = NULL;
1085 po->last_read_len = 0;
1086
1087 *action = PARSE_READ;
1088
1089 TRACE_FLOW_EXIT();
1090 return EOK;
1091 }
1092
1093 /* Process line starts with space */
handle_space(struct parser_obj * po,uint32_t * action)1094 static int handle_space(struct parser_obj *po, uint32_t *action)
1095 {
1096 int error = EOK;
1097 int space_err = 0;
1098
1099 TRACE_FLOW_ENTRY();
1100
1101 if (po->parse_flags & INI_PARSE_NOWRAP) {
1102 /* In this case an empty line is a comment. */
1103 if (is_just_spaces(po->last_read, po->last_read_len)) {
1104 error = handle_comment(po, action);
1105 TRACE_FLOW_EXIT();
1106 return error;
1107 }
1108
1109 /* Wrapping is not allowed */
1110 if (!is_allowed_spaces(po->last_read,
1111 po->last_read_len,
1112 po->parse_flags,
1113 &space_err)) {
1114 *action = PARSE_ERROR;
1115 po->last_error = space_err;
1116 error = EOK;
1117 }
1118 else {
1119 /* Allowed spaces will be trimmed
1120 * inside KVP processing.
1121 */
1122 error = handle_kvp(po, action);
1123 }
1124 TRACE_FLOW_EXIT();
1125 return error;
1126 }
1127
1128 /* Do we have current value object? */
1129 if (po->key) {
1130 /* This is a new line in a folded value */
1131 error = value_add_to_arrays(po->last_read,
1132 po->last_read_len,
1133 po->raw_lines,
1134 po->raw_lengths);
1135 if (error) {
1136 TRACE_ERROR_NUMBER("Failed to add line to value", error);
1137 return error;
1138 }
1139 /* Do not free the line, it is now an element of the array */
1140 po->last_read = NULL;
1141 po->last_read_len = 0;
1142 *action = PARSE_READ;
1143 }
1144 else {
1145 /* Check if this is a completely empty line */
1146 if (is_just_spaces(po->last_read, po->last_read_len)) {
1147 error = handle_comment(po, action);
1148 if (error) {
1149 TRACE_ERROR_NUMBER("Failed to process comment", error);
1150 return error;
1151 }
1152 }
1153 else {
1154 /* We do not have an active value
1155 * but have a line is starting with a space.
1156 * For now it is error.
1157 * We can change it in future if
1158 * people find it being too restrictive
1159 */
1160 *action = PARSE_ERROR;
1161 po->last_error = ERR_SPACE;
1162 }
1163 }
1164
1165 TRACE_FLOW_EXIT();
1166 return EOK;
1167 }
1168
1169 /* Parse and process section */
handle_section(struct parser_obj * po,uint32_t * action)1170 static int handle_section(struct parser_obj *po, uint32_t *action)
1171 {
1172 int error = EOK;
1173 char *start;
1174 char *end;
1175 char *dupval;
1176 uint32_t len;
1177
1178 TRACE_FLOW_ENTRY();
1179
1180 /* We are safe to substract 1
1181 * since we know that there is at
1182 * least one character on the line
1183 * based on the check above.
1184 */
1185 end = po->last_read + po->last_read_len - 1;
1186 while (isspace(*end)) end--;
1187 if (*end != ']') {
1188 *action = PARSE_ERROR;
1189 po->last_error = ERR_NOCLOSESEC;
1190 return EOK;
1191 }
1192
1193 /* Skip spaces at the beginning of the section name */
1194 start = po->last_read + 1;
1195 while (isspace(*start)) start++;
1196
1197 /* Check if there is a section name */
1198 if (start == end) {
1199 *action = PARSE_ERROR;
1200 po->last_error = ERR_NOSECTION;
1201 return EOK;
1202 }
1203
1204 /* Skip spaces at the end of the section name */
1205 end--;
1206 while (isspace(*end)) end--;
1207
1208 /* We got section name */
1209 len = end - start + 1;
1210
1211 if (len > MAX_KEY) {
1212 *action = PARSE_ERROR;
1213 po->last_error = ERR_SECTIONLONG;
1214 return EOK;
1215 }
1216
1217 if (po->key) {
1218 /* Complete processing of the previous value */
1219 error = complete_value_processing(po);
1220 if (error) {
1221 TRACE_ERROR_NUMBER("Failed to complete value processing", error);
1222 return error;
1223 }
1224 }
1225
1226 /* Save section if we have one*/
1227 error = parser_save_section(po);
1228 if (error) {
1229 TRACE_ERROR_NUMBER("Failed to save section", error);
1230 return error;
1231 }
1232
1233 /* Dup the name */
1234 dupval = malloc(len + 1);
1235 if (!dupval) {
1236 TRACE_ERROR_NUMBER("Failed to dup section name", ENOMEM);
1237 return ENOMEM;
1238 }
1239
1240 memcpy(dupval, start, len);
1241 dupval[len] = '\0';
1242
1243 /* Create a new section */
1244 error = col_create_collection(&po->sec,
1245 dupval,
1246 COL_CLASS_INI_SECTION);
1247 if (error) {
1248 TRACE_ERROR_NUMBER("Failed to create a section", error);
1249 free(dupval);
1250 return error;
1251 }
1252
1253 /* But if there is just a comment then create a special key */
1254 po->key_len = sizeof(INI_SECTION_KEY) - 1;
1255 po->key = strndup(INI_SECTION_KEY, sizeof(INI_SECTION_KEY));
1256 /* Create new arrays */
1257 error = value_create_arrays(&(po->raw_lines),
1258 &(po->raw_lengths));
1259 if (error) {
1260 TRACE_ERROR_NUMBER("Failed to create arrays", error);
1261 free(dupval);
1262 return error;
1263 }
1264
1265 /* Save a duplicated part in the value */
1266 error = value_add_to_arrays(dupval,
1267 len,
1268 po->raw_lines,
1269 po->raw_lengths);
1270 if (error) {
1271 TRACE_ERROR_NUMBER("Failed to add value to the arrays", error);
1272 free(dupval);
1273 return error;
1274 }
1275
1276 /* Save the line number of the last found key */
1277 po->seclinenum = po->linenum;
1278
1279 /* Complete processing of this value.
1280 * A new section will be created inside and a special
1281 * value will be added.
1282 */
1283 error = complete_value_processing(po);
1284 if (error) {
1285 TRACE_ERROR_NUMBER("Failed to complete value processing", error);
1286 return error;
1287 }
1288
1289 /* We are done dealing with section */
1290 free(po->last_read);
1291 po->last_read = NULL;
1292 po->last_read_len = 0;
1293 *action = PARSE_READ;
1294
1295 TRACE_FLOW_EXIT();
1296 return EOK;
1297
1298 }
1299
check_for_comment(char * buffer,uint32_t buffer_len,int allow_c_comments,int * inside_comment)1300 static int check_for_comment(char *buffer,
1301 uint32_t buffer_len,
1302 int allow_c_comments,
1303 int *inside_comment)
1304 {
1305 int pos;
1306 int is_comment = 0;
1307
1308 TRACE_FLOW_ENTRY();
1309
1310 if (*inside_comment) {
1311 /* We are already inside the comment
1312 * and we are looking for the end of the comment
1313 */
1314 if (buffer_len) {
1315 pos = buffer_len - 1;
1316 while(isspace(buffer[pos]) && pos > 0) pos--;
1317
1318 /* Check for comment at the end of the line */
1319 if ((pos > 1) &&
1320 (buffer[pos] == '/') &&
1321 (buffer[pos - 1] == '*')) {
1322 *inside_comment = 0;
1323 }
1324 }
1325 is_comment = 1;
1326 }
1327 else {
1328 /* We do not allow spaces in front of comments
1329 * so we expect the comment to start right away.
1330 */
1331 if ((buffer[0] == '\0') ||
1332 (buffer[0] == ';') ||
1333 (buffer[0] == '#')) {
1334 is_comment = 1;
1335 }
1336 else if ((allow_c_comments) && (buffer_len > 1)) {
1337 if (buffer[0] == '/') {
1338 if (buffer[1] == '/') is_comment = 1;
1339 else if (buffer[1] == '*') {
1340
1341 is_comment = 1;
1342 *inside_comment = 1;
1343
1344 /* Here we need to check whether this comment ends
1345 * on this line or not
1346 */
1347 pos = buffer_len - 1;
1348 while(isspace(buffer[pos]) && pos > 0) pos--;
1349
1350 /* Check for comment at the end of the line
1351 * but make sure we have at least two asterisks
1352 */
1353 if ((pos > 2) &&
1354 (buffer[pos] == '/') &&
1355 (buffer[pos - 1] == '*')) {
1356 *inside_comment = 0;
1357 }
1358 }
1359 }
1360 }
1361 }
1362
1363 TRACE_FLOW_EXIT();
1364 return is_comment;
1365 }
1366
1367 /* Inspect the line */
parser_inspect(struct parser_obj * po)1368 static int parser_inspect(struct parser_obj *po)
1369 {
1370 int error = EOK;
1371 uint32_t action = PARSE_DONE;
1372
1373 TRACE_FLOW_ENTRY();
1374
1375 TRACE_INFO_STRING("Buffer:", po->last_read);
1376 TRACE_INFO_NUMBER("In comment:", po->inside_comment);
1377
1378 if (check_for_comment(po->last_read,
1379 po->last_read_len,
1380 !(po->parse_flags & INI_PARSE_NO_C_COMMENTS),
1381 &(po->inside_comment))) {
1382
1383 error = handle_comment(po, &action);
1384 if (error) {
1385 TRACE_ERROR_NUMBER("Failed to process comment", error);
1386 return error;
1387 }
1388 }
1389 else if (isspace(*(po->last_read))) {
1390
1391 error = handle_space(po, &action);
1392 if (error) {
1393 TRACE_ERROR_NUMBER("Failed to process line wrapping", error);
1394 return error;
1395 }
1396 }
1397 else if (*(po->last_read) == '[') {
1398
1399 error = handle_section(po, &action);
1400 if (error) {
1401 TRACE_ERROR_NUMBER("Failed to save section", error);
1402 return error;
1403 }
1404 }
1405 else {
1406
1407 error = handle_kvp(po, &action);
1408 if (error) {
1409 TRACE_ERROR_NUMBER("Failed to save kvp", error);
1410 return error;
1411 }
1412 }
1413
1414 /* Move to the next action */
1415 error = col_enqueue_unsigned_property(po->queue,
1416 PARSE_ACTION,
1417 action);
1418 if (error) {
1419 TRACE_ERROR_NUMBER("Failed to schedule an action", error);
1420 return error;
1421 }
1422
1423 TRACE_FLOW_EXIT();
1424 return error;
1425 }
1426
1427
1428 /* Complete file processing */
parser_post(struct parser_obj * po)1429 static int parser_post(struct parser_obj *po)
1430 {
1431 int error = EOK;
1432
1433 TRACE_FLOW_ENTRY();
1434
1435 /* If there was just a comment at the bottom
1436 * put it directly into the config object
1437 */
1438 if((po->ic) && (!(po->key))) {
1439 if (po->co->last_comment) {
1440 error = ini_comment_add(po->ic, po->co->last_comment);
1441 if (error) {
1442 TRACE_ERROR_NUMBER("Failed to merge comment", error);
1443 return error;
1444 }
1445 }
1446 else {
1447 error = ini_comment_copy(po->ic, &(po->co->last_comment));
1448 if (error) {
1449 TRACE_ERROR_NUMBER("Failed to copy comment", error);
1450 return error;
1451 }
1452 }
1453
1454 ini_comment_destroy(po->ic);
1455 po->ic = NULL;
1456 }
1457
1458 /* If there is a key being processed add it */
1459 if (po->key) {
1460 error = complete_value_processing(po);
1461 if (error) {
1462 TRACE_ERROR_NUMBER("Failed to complete value processing", error);
1463 return error;
1464 }
1465 }
1466
1467 /* If we are done save the section */
1468 error = parser_save_section(po);
1469 if (error) {
1470 TRACE_ERROR_NUMBER("Failed to save section", error);
1471 return error;
1472 }
1473
1474 /* Move to the next action */
1475 error = col_enqueue_unsigned_property(po->queue,
1476 PARSE_ACTION,
1477 PARSE_DONE);
1478 if (error) {
1479 TRACE_ERROR_NUMBER("Failed to schedule an action", error);
1480 return error;
1481 }
1482
1483 TRACE_FLOW_EXIT();
1484 return EOK;
1485 }
1486
1487
save_error(struct collection_item * el,unsigned line,int inerr,const char * err_txt)1488 static int save_error(struct collection_item *el,
1489 unsigned line,
1490 int inerr,
1491 const char *err_txt)
1492 {
1493 int error = EOK;
1494 struct ini_parse_error pe;
1495
1496 TRACE_FLOW_ENTRY();
1497
1498 /* Clear the warning bit */
1499 pe.error = inerr;
1500 pe.line = line;
1501 error = col_add_binary_property(el, NULL,
1502 err_txt, &pe, sizeof(pe));
1503 TRACE_FLOW_RETURN(error);
1504 return error;
1505 }
1506
1507
1508 /* Error and warning processing */
parser_error(struct parser_obj * po)1509 static int parser_error(struct parser_obj *po)
1510 {
1511 int error = EOK;
1512 uint32_t action;
1513 const char *err_str;
1514
1515 TRACE_FLOW_ENTRY();
1516
1517 if (po->last_error & INI_WARNING) err_str = WARNING_TXT;
1518 else err_str = ERROR_TXT;
1519
1520 error = save_error(po->el,
1521 po->linenum,
1522 po->last_error & ~INI_WARNING,
1523 err_str);
1524 if (error) {
1525 TRACE_ERROR_NUMBER("Failed to add error to error list",
1526 error);
1527 return error;
1528 }
1529
1530 if (po->last_error == ERR_BADCOMMENT) {
1531 /* Avoid endless loop */
1532 action = PARSE_DONE;
1533 po->ret = EIO;
1534 }
1535 else if (po->error_level == INI_STOP_ON_ANY) {
1536 action = PARSE_DONE;
1537 if (po->last_error & INI_WARNING) po->ret = EILSEQ;
1538 else po->ret = EIO;
1539 }
1540 else if (po->error_level == INI_STOP_ON_NONE) {
1541 if (po->last_error != ERR_READ) {
1542 action = PARSE_READ;
1543 if (po->ret == 0) {
1544 if (po->last_error & INI_WARNING) po->ret = EILSEQ;
1545 else po->ret = EIO;
1546 }
1547 /* It it was warning but now if it is an error
1548 * bump to return code to indicate error. */
1549 else if((po->ret == EILSEQ) &&
1550 (!(po->last_error & INI_WARNING))) po->ret = EIO;
1551 }
1552 else {
1553 /* Avoid endless loop */
1554 action = PARSE_DONE;
1555 po->ret = EIO;
1556 }
1557 }
1558 else { /* Stop on error */
1559 if (po->last_error & INI_WARNING) {
1560 action = PARSE_READ;
1561 po->ret = EILSEQ;
1562 }
1563 else {
1564 action = PARSE_DONE;
1565 po->ret = EIO;
1566 }
1567 }
1568
1569 /* Prepare for reading */
1570 if (action == PARSE_READ) {
1571 if (po->last_read) {
1572 free(po->last_read);
1573 po->last_read = NULL;
1574 po->last_read_len = 0;
1575 }
1576 }
1577 else {
1578 /* If we are done save the section */
1579 error = parser_save_section(po);
1580 if (error) {
1581 TRACE_ERROR_NUMBER("Failed to save section", error);
1582 /* If merging sections should produce error and we got error
1583 * or if we merge sections but dup values produce error and
1584 * we got error then it is not a fatal error so we need to handle
1585 * it nicely and suppress it here. We already in the procees
1586 * of handling another error and merge error does not matter here.
1587 * We check for reverse condition and return error,
1588 * otherwise fall through.
1589 */
1590 if (!(((ini_flags_have(INI_MS_ERROR, po->collision_flags)) &&
1591 (error == EEXIST)) ||
1592 (ini_flags_have(INI_MS_ERROR, po->collision_flags) &&
1593 ((po->collision_flags & INI_MV2S_MASK) == INI_MV2S_ERROR) &&
1594 (error == EEXIST)))) {
1595 return error;
1596 }
1597 }
1598 }
1599
1600 /* Move to the next action */
1601 error = col_enqueue_unsigned_property(po->queue,
1602 PARSE_ACTION,
1603 action);
1604 if (error) {
1605 TRACE_ERROR_NUMBER("Failed to schedule an action", error);
1606 return error;
1607 }
1608
1609 TRACE_FLOW_EXIT();
1610 return EOK;
1611 }
1612
1613
1614 /* Run parser */
parser_run(struct parser_obj * po)1615 static int parser_run(struct parser_obj *po)
1616 {
1617 int error = EOK;
1618 struct collection_item *item = NULL;
1619 uint32_t action = 0;
1620 action_fn operations[] = { parser_read,
1621 parser_inspect,
1622 parser_post,
1623 parser_error,
1624 NULL };
1625
1626 TRACE_FLOW_ENTRY();
1627
1628 while(1) {
1629 /* Get next action */
1630 item = NULL;
1631 error = col_dequeue_item(po->queue, &item);
1632 if (error) {
1633 TRACE_ERROR_NUMBER("Failed to get action", error);
1634 return error;
1635 }
1636
1637 /* Get action, run operation */
1638 action = *((uint32_t *)(col_get_item_data(item)));
1639 col_delete_item(item);
1640
1641 if (action == PARSE_DONE) {
1642
1643 TRACE_INFO_NUMBER("We are done", error);
1644
1645 /* Report merge error in detect mode
1646 * if no other error was detected. */
1647 if ((po->ret == 0) &&
1648 (po->merge_error != 0) &&
1649 ((po->collision_flags & INI_MV1S_DETECT) ||
1650 (po->collision_flags & INI_MV2S_DETECT) ||
1651 (po->collision_flags & INI_MS_DETECT)))
1652 po->ret = po->merge_error;
1653
1654 error = po->ret;
1655 break;
1656 }
1657
1658 error = operations[action](po);
1659 if (error) {
1660 TRACE_ERROR_NUMBER("Failed to perform an action", error);
1661 return error;
1662 }
1663
1664 }
1665
1666 TRACE_FLOW_EXIT();
1667 return error;
1668 }
1669
1670 /* Top level wrapper around the parser */
ini_config_parse(struct ini_cfgfile * file_ctx,int error_level,uint32_t collision_flags,uint32_t parse_flags,struct ini_cfgobj * ini_config)1671 int ini_config_parse(struct ini_cfgfile *file_ctx,
1672 int error_level,
1673 uint32_t collision_flags,
1674 uint32_t parse_flags,
1675 struct ini_cfgobj *ini_config)
1676 {
1677 int error = EOK;
1678 struct parser_obj *po = NULL;
1679 uint32_t fl1, fl2, fl3;
1680
1681 TRACE_FLOW_ENTRY();
1682
1683 if ((!ini_config) || (!(ini_config->cfg))) {
1684 TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
1685 return EINVAL;
1686 }
1687
1688 if (!file_ctx) {
1689 TRACE_ERROR_NUMBER("Invalid file context", EINVAL);
1690 return EINVAL;
1691 }
1692
1693 if (!valid_collision_flags(collision_flags)) {
1694 TRACE_ERROR_NUMBER("Invalid flags.", EINVAL);
1695 return EINVAL;
1696 }
1697
1698 if ((error_level != INI_STOP_ON_ANY) &&
1699 (error_level != INI_STOP_ON_NONE) &&
1700 (error_level != INI_STOP_ON_ERROR)) {
1701 TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
1702 return EINVAL;
1703 }
1704
1705 error = parser_create(ini_config,
1706 file_ctx->file,
1707 file_ctx->filename,
1708 error_level,
1709 collision_flags,
1710 parse_flags,
1711 &po);
1712 if (error) {
1713 TRACE_ERROR_NUMBER("Failed to perform an action", error);
1714 return error;
1715 }
1716
1717 error = parser_run(po);
1718 if (error) {
1719 fl1 = collision_flags & INI_MS_MODE_MASK;
1720 fl2 = collision_flags & INI_MV1S_MASK;
1721 fl3 = collision_flags & INI_MV2S_MASK;
1722 if ((error == EEXIST) &&
1723 ((ini_flags_have(INI_MS_DETECT, collision_flags) &&
1724 (fl1 != INI_MS_ERROR) &&
1725 (fl2 != INI_MV1S_ERROR) &&
1726 (fl3 != INI_MV2S_ERROR)) ||
1727 ((fl2 == INI_MV1S_DETECT) &&
1728 (fl1 != INI_MS_ERROR) &&
1729 (fl3 != INI_MV2S_ERROR)) ||
1730 ((fl3 == INI_MV2S_DETECT) &&
1731 (fl1 != INI_MS_ERROR) &&
1732 (fl2 != INI_MV1S_ERROR)))) {
1733 TRACE_ERROR_NUMBER("No error in detect mode", error);
1734 /* Fall through */
1735 }
1736 else {
1737 TRACE_ERROR_NUMBER("Failed to parse file", error);
1738 TRACE_ERROR_NUMBER("Mode", collision_flags);
1739 col_get_collection_count(ini_config->error_list, &(ini_config->count));
1740 if(ini_config->count) (ini_config->count)--;
1741 parser_destroy(po);
1742 return error;
1743 }
1744 }
1745
1746 /* If should be empty anyways */
1747 col_destroy_collection_with_cb(ini_config->cfg, ini_cleanup_cb, NULL);
1748 ini_config->cfg = po->top;
1749 po->top = NULL;
1750
1751 parser_destroy(po);
1752
1753 TRACE_FLOW_EXIT();
1754 return error;
1755 }
1756