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