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