1 /*
2 INI LIBRARY
3
4 Reading configuration from INI file
5 and storing as a collection.
6
7 Copyright (C) Dmitri Pal <dpal@redhat.com> 2009
8
9 INI Library is free software: you can redistribute it and/or modify
10 it under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 INI Library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public License
20 along with INI Library. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #define _GNU_SOURCE
24 #include <stdio.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <stdlib.h>
29 #include <locale.h>
30 #include <fcntl.h>
31 #include "config.h"
32 /* For error text */
33 #include <libintl.h>
34 #define _(String) gettext (String)
35 /* INI file is used as a collection */
36 #include "collection.h"
37 #include "collection_tools.h"
38 #include "trace.h"
39 #include "ini_config.h"
40
41 #define NAME_OVERHEAD 10
42
43 #define SLASH "/"
44
45 #define EXCLUDE_EMPTY 0
46 #define INCLUDE_EMPTY 1
47
48 /* Name of the special collection used to store parsing errors */
49 #define FILE_ERROR_SET "ini_file_error_set"
50
51 /* Text error strings used when errors are printed out */
52 #define WARNING_TXT _("Warning")
53 #define ERROR_TXT _("Error")
54 /* For parse errors */
55 #define WRONG_COLLECTION _("Passed in list is not a list of parse errors.\n")
56 #define FAILED_TO_PROCCESS _("Internal Error. Failed to process error list.\n")
57 #define ERROR_HEADER _("Parsing errors and warnings in file: %s\n")
58 /* For grammar errors */
59 #define WRONG_GRAMMAR _("Passed in list is not a list of grammar errors.\n")
60 #define FAILED_TO_PROC_G _("Internal Error. Failed to process list of grammar errors.\n")
61 #define ERROR_HEADER_G _("Logical errors and warnings in file: %s\n")
62 /* For validation errors */
63 #define WRONG_VALIDATION _("Passed in list is not a list of validation errors.\n")
64 #define FAILED_TO_PROC_V _("Internal Error. Failed to process list of validation errors.\n")
65 #define ERROR_HEADER_V _("Validation errors and warnings in file: %s\n")
66
67 #define LINE_FORMAT _("%s (%d) on line %d: %s\n")
68
69
70 /* Codes that parsing function can return */
71 #define RET_PAIR 0
72 #define RET_COMMENT 1
73 #define RET_SECTION 2
74 #define RET_INVALID 3
75 #define RET_EMPTY 4
76 #define RET_EOF 5
77 #define RET_ERROR 6
78
79 #define INI_ERROR "errors"
80 #define INI_ERROR_NAME "errname"
81
82
83 /* Internal sizes. MAX_KEY is defined in config.h */
84 #define MAX_VALUE PATH_MAX
85 #define BUFFER_SIZE MAX_KEY + MAX_VALUE + 3
86
87 /* Internally used functions */
88 static int config_with_lines(const char *application,
89 FILE *config_file,
90 const char *config_source,
91 struct collection_item **ini_config,
92 int error_level,
93 struct collection_item **error_list,
94 struct collection_item **lines);
95
96
97
98 /* Different error string functions can be passed as callbacks */
99 typedef const char * (*error_fn)(int error);
100
101 /* Function to return parsing error */
102 const char *parsing_error_str(int parsing_error)
103 {
104 const char *placeholder= _("Unknown pasing error.");
105 const char *str_error[] = { _("Data is too long."),
106 _("No closing bracket."),
107 _("Section name is missing."),
108 _("Section name is too long."),
109 _("Equal sign is missing."),
110 _("Property name is missing."),
111 _("Property name is too long.")
112 };
113
114 /* Check the range */
115 if ((parsing_error < 1) || (parsing_error > ERR_MAXPARSE))
116 return placeholder;
117 else
118 return str_error[parsing_error-1];
119 }
120
121 /* Function to return grammar error.
122 * This function is currently not used.
123 * It is planned to be used by the INI
124 * file grammar parser.
125 */
126 const char *grammar_error_str(int grammar_error)
127 {
128 const char *placeholder= _("Unknown grammar error.");
129 /* THIS IS A TEMPORARY PLACEHOLDER !!!! */
130 const char *str_error[] = { _(""),
131 _(""),
132 _(""),
133 _(""),
134 _(""),
135 _(""),
136 _("")
137 };
138
139 /* Check the range */
140 if ((grammar_error < 1) || (grammar_error > ERR_MAXGRAMMAR))
141 return placeholder;
142 else
143 return str_error[grammar_error-1];
144 }
145
146 /* Function to return validation error.
147 * This function is currently not used.
148 * It is planned to be used by the INI
149 * file grammar validator.
150 */
151 const char *validation_error_str(int validation_error)
152 {
153 const char *placeholder= _("Unknown validation error.");
154 /* THIS IS A TEMPORARY PLACEHOLDER !!!! */
155 const char *str_error[] = { _(""),
156 _(""),
157 _(""),
158 _(""),
159 _(""),
160 _(""),
161 _("")
162 };
163
164 /* Check the range */
165 if ((validation_error < 1) || (validation_error > ERR_MAXVALID))
166 return placeholder;
167 else
168 return str_error[validation_error-1];
169 }
170
171
172 /* Internal function to read line from INI file */
173 int read_line(FILE *file,
174 char *buf,
175 int read_size,
176 char **key,
177 char **value,
178 int *length,
179 int *ext_error);
180
181 /***************************************************************************/
182 /* Function to read single ini file and pupulate
183 * the provided collection with subcollcetions from the file */
184 static int ini_to_collection(FILE *file,
185 const char *config_filename,
186 struct collection_item *ini_config,
187 int error_level,
188 struct collection_item **error_list,
189 struct collection_item **lines)
190 {
191 int error;
192 int status;
193 int section_count = 0;
194 char *key = NULL;
195 char *value = NULL;
196 struct collection_item *current_section = NULL;
197 int length;
198 int ext_err = -1;
199 struct parse_error pe;
200 int line = 0;
201 int created = 0;
202 char buf[BUFFER_SIZE+1];
203
204
205 TRACE_FLOW_STRING("ini_to_collection", "Entry");
206
207 if (file == NULL) {
208 TRACE_ERROR_NUMBER("No file handle", EINVAL);
209 return EINVAL;
210 }
211
212 /* Open the collection of errors */
213 if (error_list != NULL) {
214 *error_list = NULL;
215 error = col_create_collection(error_list, INI_ERROR, COL_CLASS_INI_PERROR);
216 if (error) {
217 TRACE_ERROR_NUMBER("Failed to create error collection", error);
218 fclose(file);
219 return error;
220 }
221 /* Add file name as the first item */
222 error = col_add_str_property(*error_list, NULL, INI_ERROR_NAME, config_filename, 0);
223 if (error) {
224 TRACE_ERROR_NUMBER("Failed to and name to collection", error);
225 fclose(file);
226 col_destroy_collection(*error_list);
227 return error;
228 }
229 created = 1;
230 }
231
232 /* Read file lines */
233 while (1) {
234 /* Always read one less than the buffer */
235 status = read_line(file, buf, BUFFER_SIZE+1, &key, &value, &length, &ext_err);
236 if (status == RET_EOF) break;
237
238 line++;
239
240 switch (status) {
241 case RET_PAIR:
242 /* Add line to the collection of lines.
243 * It is pretty safe in this case to just type cast the value to
244 * int32_t since it is unrealistic that ini file will ever have
245 * so many lines.
246 */
247 if (lines) {
248 error = col_add_int_property(*lines, NULL, key, (int32_t)line);
249 if (error) {
250 TRACE_ERROR_NUMBER("Failed to add line to line collection", error);
251 fclose(file);
252 col_destroy_collection(current_section);
253 if (created) {
254 col_destroy_collection(*error_list);
255 *error_list = NULL;
256 }
257 return error;
258 }
259 }
260
261 /* Do we have a section at the top of the file ? */
262 if (section_count == 0) {
263 /* Check if collection already exists */
264 error = col_get_collection_reference(ini_config, ¤t_section,
265 INI_DEFAULT_SECTION);
266 if (error != EOK) {
267 /* Create default collection */
268 if ((error = col_create_collection(¤t_section,
269 INI_DEFAULT_SECTION,
270 COL_CLASS_INI_SECTION)) ||
271 (error = col_add_collection_to_collection(ini_config,
272 NULL,NULL,
273 current_section,
274 COL_ADD_MODE_REFERENCE))) {
275 TRACE_ERROR_NUMBER("Failed to create collection", error);
276 fclose(file);
277 col_destroy_collection(current_section);
278 if (created) {
279 col_destroy_collection(*error_list);
280 *error_list = NULL;
281 }
282 return error;
283 }
284 }
285 section_count++;
286 }
287
288 /* Put value into the collection */
289 error = col_insert_str_property(current_section,
290 NULL,
291 COL_DSP_END,
292 NULL,
293 0,
294 COL_INSERT_DUPOVER,
295 key,
296 value,
297 length);
298 if (error != EOK) {
299 TRACE_ERROR_NUMBER("Failed to add pair to collection", error);
300 fclose(file);
301 col_destroy_collection(current_section);
302 if (created) {
303 col_destroy_collection(*error_list);
304 *error_list = NULL;
305 }
306 return error;
307 }
308 break;
309
310 case RET_SECTION:
311 /* Add line to the collection of lines */
312 if (lines) {
313 /* For easier search make line numbers for the sections negative.
314 * This would allow differentiating sections and attributes.
315 * It is pretty safe in this case to just type cast the value to
316 * int32_t since it is unrealistic that ini file will ever have
317 * so many lines.
318 */
319 error = col_add_int_property(*lines, NULL, key, (int32_t)(-1 * line));
320 if (error) {
321 TRACE_ERROR_NUMBER("Failed to add line to line collection", error);
322 fclose(file);
323 col_destroy_collection(current_section);
324 if (created) {
325 col_destroy_collection(*error_list);
326 *error_list = NULL;
327 }
328 return error;
329 }
330 }
331
332 /* Read a new section */
333 col_destroy_collection(current_section);
334 current_section = NULL;
335
336 error = col_get_collection_reference(ini_config, ¤t_section, key);
337 if (error != EOK) {
338 /* Create default collection */
339 if ((error = col_create_collection(¤t_section, key,
340 COL_CLASS_INI_SECTION)) ||
341 (error = col_add_collection_to_collection(ini_config,
342 NULL, NULL,
343 current_section,
344 COL_ADD_MODE_REFERENCE))) {
345 TRACE_ERROR_NUMBER("Failed to add collection", error);
346 fclose(file);
347 col_destroy_collection(current_section);
348 if (created) {
349 col_destroy_collection(*error_list);
350 *error_list = NULL;
351 }
352 return error;
353 }
354 }
355 section_count++;
356 break;
357
358 case RET_EMPTY:
359 TRACE_INFO_STRING("Empty string", "");
360 break;
361
362 case RET_COMMENT:
363 TRACE_INFO_STRING("Comment", "");
364 break;
365
366 case RET_ERROR:
367 pe.line = line;
368 pe.error = ext_err;
369 error = col_add_binary_property(*error_list, NULL,
370 ERROR_TXT, &pe, sizeof(pe));
371 if (error) {
372 TRACE_ERROR_NUMBER("Failed to add error to collection", error);
373 fclose(file);
374 col_destroy_collection(current_section);
375 if (created) {
376 col_destroy_collection(*error_list);
377 *error_list = NULL;
378 }
379 return error;
380 }
381 /* Exit if there was an error parsing file */
382 if (error_level != INI_STOP_ON_NONE) {
383 TRACE_ERROR_STRING("Invalid format of the file", "");
384 col_destroy_collection(current_section);
385 fclose(file);
386 return EIO;
387 }
388 break;
389
390 case RET_INVALID:
391 default:
392 pe.line = line;
393 pe.error = ext_err;
394 error = col_add_binary_property(*error_list, NULL,
395 WARNING_TXT, &pe, sizeof(pe));
396 if (error) {
397 TRACE_ERROR_NUMBER("Failed to add warning to collection", error);
398 fclose(file);
399 col_destroy_collection(current_section);
400 if (created) {
401 col_destroy_collection(*error_list);
402 *error_list = NULL;
403 }
404 return error;
405 }
406 /* Exit if we are told to exit on warnings */
407 if (error_level == INI_STOP_ON_ANY) {
408 TRACE_ERROR_STRING("Invalid format of the file", "");
409 if (created) col_destroy_collection(current_section);
410 fclose(file);
411 return EIO;
412 }
413 TRACE_ERROR_STRING("Invalid string", "");
414 break;
415 }
416 ext_err = -1;
417 }
418
419 /* Close file */
420 fclose(file);
421
422 COL_DEBUG_COLLECTION(ini_config);
423
424 col_destroy_collection(current_section);
425
426 COL_DEBUG_COLLECTION(ini_config);
427
428 TRACE_FLOW_STRING("ini_to_collection", "Success Exit");
429
430 return EOK;
431 }
432
433 /*********************************************************************/
434 /* Function to free configuration */
435 void free_ini_config(struct collection_item *ini_config)
436 {
437 TRACE_FLOW_STRING("free_ini_config", "Entry");
438 col_destroy_collection(ini_config);
439 TRACE_FLOW_STRING("free_ini_config", "Exit");
440 }
441
442 /* Function to free configuration error list */
443 void free_ini_config_errors(struct collection_item *error_set)
444 {
445 TRACE_FLOW_STRING("free_ini_config_errors", "Entry");
446 col_destroy_collection(error_set);
447 TRACE_FLOW_STRING("free_ini_config_errors", "Exit");
448 }
449
450 /* Function to free configuration lines list */
451 void free_ini_config_lines(struct collection_item *lines)
452 {
453 TRACE_FLOW_STRING("free_ini_config_lines", "Entry");
454 col_destroy_collection(lines);
455 TRACE_FLOW_STRING("free_ini_config_lines", "Exit");
456 }
457
458
459 /* Read configuration information from a file */
460 int config_from_file(const char *application,
461 const char *config_filename,
462 struct collection_item **ini_config,
463 int error_level,
464 struct collection_item **error_list)
465 {
466 int error;
467
468 TRACE_FLOW_STRING("config_from_file", "Entry");
469 error = config_from_file_with_lines(application,
470 config_filename,
471 ini_config,
472 error_level,
473 error_list,
474 NULL);
475 TRACE_FLOW_NUMBER("config_from_file. Returns", error);
476 return error;
477 }
478
479 /* Read configuration information from a file descriptor */
480 int config_from_fd(const char *application,
481 int fd,
482 const char *config_source,
483 struct collection_item **ini_config,
484 int error_level,
485 struct collection_item **error_list)
486 {
487 int error;
488
489 TRACE_FLOW_STRING("config_from_fd", "Entry");
490 error = config_from_fd_with_lines(application, fd, config_source,
491 ini_config, error_level,
492 error_list, NULL);
493 TRACE_FLOW_NUMBER("config_from_fd. Returns", error);
494 return error;
495 }
496
497 /* Function to read the ini file and have a collection
498 * of which item appers on which line
499 */
500 int config_from_file_with_lines(const char *application,
501 const char *config_filename,
502 struct collection_item **ini_config,
503 int error_level,
504 struct collection_item **error_list,
505 struct collection_item **lines)
506 {
507 int error = EOK;
508 FILE *config_file = NULL;
509
510 TRACE_FLOW_STRING("config_from_file_with_lines", "Entry");
511
512 config_file = fopen(config_filename, "r");
513 if(!config_file) {
514 error = errno;
515 TRACE_ERROR_NUMBER("Failed to open file", error);
516 return error;
517 }
518
519 error = config_with_lines(application, config_file,
520 config_filename, ini_config,
521 error_level, error_list,
522 lines);
523 TRACE_FLOW_NUMBER("config_from_file_with_lines. Returns", error);
524 return error;
525 }
526
527 /* Function to read the ini file and have a collection
528 * of which item appers on which line
529 */
530 int config_from_fd_with_lines(const char *application,
531 int fd,
532 const char *config_source,
533 struct collection_item **ini_config,
534 int error_level,
535 struct collection_item **error_list,
536 struct collection_item **lines)
537 {
538 int error = EOK;
539 FILE *config_file;
540
541 TRACE_FLOW_STRING("config_from_fd_with_lines", "Entry");
542
543 config_file = fdopen(fd, "r");
544 if (!config_file) {
545 error = errno;
546 TRACE_ERROR_NUMBER("Failed to dup file", error);
547 return error;
548 }
549
550 error = config_with_lines(application, config_file,
551 config_source, ini_config,
552 error_level, error_list,
553 lines);
554 TRACE_FLOW_NUMBER("config_from_fd_with_lines. Returns", error);
555
556 return error;
557 }
558
559 /* Low level function that prepares the collection
560 * and calls parser.
561 */
562 static int config_with_lines(const char *application,
563 FILE *config_file,
564 const char *config_source,
565 struct collection_item **ini_config,
566 int error_level,
567 struct collection_item **error_list,
568 struct collection_item **lines)
569 {
570 int error;
571 int created = 0;
572 int created_lines = 0;
573
574 TRACE_FLOW_STRING("config_from_file", "Entry");
575
576 if ((ini_config == NULL) ||
577 (application == NULL)) {
578 TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
579 return EINVAL;
580 }
581
582 /* Create collection if needed */
583 if (*ini_config == NULL) {
584 error = col_create_collection(ini_config,
585 application,
586 COL_CLASS_INI_CONFIG);
587 if (error != EOK) {
588 TRACE_ERROR_NUMBER("Failed to create collection", error);
589 return error;
590 }
591 created = 1;
592 }
593 /* Is the collection of the right class? */
594 else if (col_is_of_class(*ini_config, COL_CLASS_INI_CONFIG)) {
595 TRACE_ERROR_NUMBER("Wrong collection type", EINVAL);
596 return EINVAL;
597 }
598
599
600 /* Create collection if needed */
601 if (lines) {
602
603 /* Make sure that the lines collection is empty */
604 if (*lines) {
605 TRACE_ERROR_NUMBER("Collection of lines is not empty", EINVAL);
606 if (created) {
607 col_destroy_collection(*ini_config);
608 *ini_config = NULL;
609 }
610 return EINVAL;
611 }
612
613 error = col_create_collection(lines,
614 application,
615 COL_CLASS_INI_LINES);
616 if (error != EOK) {
617 TRACE_ERROR_NUMBER("Failed to create collection", error);
618 if (created) {
619 col_destroy_collection(*ini_config);
620 *ini_config = NULL;
621 }
622 return error;
623 }
624 created_lines = 1;
625 }
626
627 /* Do the actual work */
628 error = ini_to_collection(config_file, config_source,
629 *ini_config, error_level,
630 error_list, lines);
631 /* In case of error when we created collection - delete it */
632 if (error && created) {
633 col_destroy_collection(*ini_config);
634 *ini_config = NULL;
635 }
636 /* Also create collection of lines if we created it */
637 if (error && created_lines) {
638 col_destroy_collection(*lines);
639 *lines = NULL;
640 }
641
642 TRACE_FLOW_NUMBER("config_from_file. Returns", error);
643 return error;
644 }
645
646 /* Special wrapper around the inernal parser
647 * to open the file first.
648 * Used in conf_for_app function.
649 */
650 static int ini_to_col_from_file(const char *config_filename,
651 struct collection_item *ini_config,
652 int error_level,
653 struct collection_item **error_list,
654 struct collection_item **lines)
655 {
656 int error = EOK;
657 FILE *config_file = NULL;
658
659 TRACE_FLOW_STRING("ini_to_col_from_file", "Entry");
660
661 config_file = fopen(config_filename, "r");
662 if(!config_file) {
663 error = errno;
664 TRACE_ERROR_NUMBER("ini_to_col_from_file. Returns", error);
665 return ENOENT;
666 }
667
668 error = ini_to_collection(config_file,
669 config_filename,
670 ini_config,
671 error_level,
672 error_list,
673 lines);
674 TRACE_FLOW_NUMBER("ini_to_col_from_file. Returns", error);
675 return error;
676 }
677
678
679 /* Read default config file and then overwrite it with a specific one
680 * from the directory */
681 int config_for_app(const char *application,
682 const char *config_file,
683 const char *config_dir,
684 struct collection_item **ini_config,
685 int error_level,
686 struct collection_item **error_set)
687 {
688 int error = EOK;
689 char *file_name;
690 struct collection_item *error_list_common = NULL;
691 struct collection_item *error_list_specific = NULL;
692 struct collection_item **pass_common = NULL;
693 struct collection_item **pass_specific = NULL;
694 int created = 0;
695 int tried = 0;
696 int noents = 0;
697
698 TRACE_FLOW_STRING("config_to_collection", "Entry");
699
700 if (ini_config == NULL) {
701 TRACE_ERROR_NUMBER("Invalid parameter", EINVAL);
702 return EINVAL;
703 }
704
705 if ((config_file == NULL) && (config_dir == NULL)) {
706 TRACE_ERROR_NUMBER("Noop call of the function is invalid", EINVAL);
707 return EINVAL;
708 }
709
710 /* Prepare error collection pointers */
711 if (error_set != NULL) {
712 TRACE_INFO_STRING("Error set is not NULL", "preparing error set");
713 pass_common = &error_list_common;
714 pass_specific = &error_list_specific;
715 *error_set = NULL;
716 /* Construct the overarching error collection */
717 error = col_create_collection(error_set,
718 FILE_ERROR_SET,
719 COL_CLASS_INI_PESET);
720 if (error != EOK) {
721 TRACE_ERROR_NUMBER("Failed to create collection", error);
722 return error;
723 }
724 }
725 else {
726 TRACE_INFO_STRING("No error set. Errors will not be captured", "");
727 pass_common = NULL;
728 pass_specific = NULL;
729 }
730
731 /* Create collection if needed */
732 if (*ini_config == NULL) {
733 TRACE_INFO_STRING("New config collection. Allocate.", "");
734 error = col_create_collection(ini_config,
735 application,
736 COL_CLASS_INI_CONFIG);
737 if (error != EOK) {
738 TRACE_ERROR_NUMBER("Failed to create collection", error);
739 if (error_set) {
740 col_destroy_collection(*error_set);
741 *error_set = NULL;
742 }
743 return error;
744 }
745 created = 1;
746 }
747 /* Is the collection of the right class? */
748 else if (col_is_of_class(*ini_config, COL_CLASS_INI_CONFIG)) {
749 TRACE_ERROR_NUMBER("Wrong collection type", EINVAL);
750 return EINVAL;
751 }
752
753 /* Read master file */
754 if (config_file != NULL) {
755 TRACE_INFO_STRING("Reading master file:", config_file);
756 error = ini_to_col_from_file(config_file, *ini_config,
757 error_level, pass_common, NULL);
758 tried++;
759 /* ENOENT and EOK are Ok */
760 if (error) {
761 if (error != ENOENT) {
762 TRACE_ERROR_NUMBER("Failed to read master file", error);
763 /* In case of error when we created collection - delete it */
764 if(error && created) {
765 col_destroy_collection(*ini_config);
766 *ini_config = NULL;
767 }
768 /* We do not clear the error_set here */
769 return error;
770 }
771 else noents++;
772 }
773 /* Add error results if any to the overarching error collection */
774 if ((pass_common != NULL) && (*pass_common != NULL)) {
775 TRACE_INFO_STRING("Process errors resulting from file:", config_file);
776 error = col_add_collection_to_collection(*error_set, NULL, NULL,
777 *pass_common,
778 COL_ADD_MODE_EMBED);
779 if (error) {
780 if (created) {
781 col_destroy_collection(*ini_config);
782 *ini_config = NULL;
783 }
784 if (error_set) {
785 col_destroy_collection(*error_set);
786 *error_set = NULL;
787 }
788 TRACE_ERROR_NUMBER("Failed to add error collection to another error collection", error);
789 return error;
790 }
791 }
792 }
793
794 if (config_dir != NULL) {
795 /* Get specific application file */
796 file_name = malloc(strlen(config_dir) + strlen(application) + NAME_OVERHEAD);
797 if (file_name == NULL) {
798 error = ENOMEM;
799 TRACE_ERROR_NUMBER("Failed to allocate memory for file name", error);
800 /* In case of error when we created collection - delete it */
801 if(created) {
802 col_destroy_collection(*ini_config);
803 *ini_config = NULL;
804 }
805 if (error_set) {
806 col_destroy_collection(*error_set);
807 *error_set = NULL;
808 }
809 return error;
810 }
811
812 sprintf(file_name, "%s%s%s.conf", config_dir, SLASH, application);
813 TRACE_INFO_STRING("Opening file:", file_name);
814 /* Read specific file */
815 error = ini_to_col_from_file(file_name, *ini_config,
816 error_level, pass_specific, NULL);
817 tried++;
818 free(file_name);
819 /* ENOENT and EOK are Ok */
820 if (error) {
821 if (error != ENOENT) {
822 TRACE_ERROR_NUMBER("Failed to read specific application file", error);
823 /* In case of error when we created collection - delete it */
824 if (error && created) {
825 col_destroy_collection(*ini_config);
826 *ini_config = NULL;
827 }
828 /* We do not clear the error_set here */
829 return error;
830 }
831 else noents++;
832 }
833 /* Add error results if any to the overarching error collection */
834 if ((pass_specific != NULL) && (*pass_specific != NULL)) {
835 error = col_add_collection_to_collection(*error_set, NULL, NULL,
836 *pass_specific,
837 COL_ADD_MODE_EMBED);
838 if (error) {
839 if (created) {
840 col_destroy_collection(*ini_config);
841 *ini_config = NULL;
842 }
843 if (error_set) {
844 col_destroy_collection(*error_set);
845 *error_set = NULL;
846 }
847 TRACE_ERROR_NUMBER("Failed to add error collection to another error collection", error);
848 return error;
849 }
850 }
851 }
852
853 /* If we failed to read or access file as many
854 * times as we tried and we told to stop on any errors
855 * we should report an error.
856 */
857 TRACE_INFO_NUMBER("Tried:", tried);
858 TRACE_INFO_NUMBER("Noents:", noents);
859
860 if ((tried == noents) && (error_level == INI_STOP_ON_ANY)) {
861 TRACE_ERROR_NUMBER("Fail to read or access all the files tried", ENOENT);
862 if (created) {
863 col_destroy_collection(*ini_config);
864 *ini_config = NULL;
865 }
866 if (error_set) {
867 col_destroy_collection(*error_set);
868 *error_set = NULL;
869 }
870 return ENOENT;
871 }
872
873 TRACE_FLOW_STRING("config_to_collection", "Exit");
874 return EOK;
875 }
876
877 /* Reads a line from the file */
878 int read_line(FILE *file,
879 char *buf,
880 int read_size,
881 char **key, char **value,
882 int *length,
883 int *ext_error)
884 {
885
886 char *res;
887 int len;
888 char *buffer;
889 int i;
890 char *eq;
891
892 TRACE_FLOW_STRING("read_line", "Entry");
893
894 *ext_error = 0;
895
896 buffer = buf;
897
898 /* Get data from file */
899 res = fgets(buffer, read_size - 1, file);
900 if (res == NULL) {
901 TRACE_ERROR_STRING("Read nothing", "");
902 return RET_EOF;
903 }
904
905 /* Make sure the buffer is NULL terminated */
906 buffer[read_size - 1] = '\0';
907
908 len = strlen(buffer);
909 if (len == 0) {
910 TRACE_ERROR_STRING("Nothing was read.", "");
911 return RET_EMPTY;
912 }
913
914 /* Added \r just in case we deal with Windows in future */
915 if ((buffer[len - 1] != '\n') && (buffer[len - 1] != '\r')) {
916 TRACE_ERROR_STRING("String it too big!", "");
917 *ext_error = ERR_LONGDATA;
918 return RET_ERROR;
919 }
920
921 /* Ingnore comments */
922 if ((*buffer == ';') || (*buffer == '#')) {
923 TRACE_FLOW_STRING("Comment", buf);
924 return RET_COMMENT;
925 }
926
927 TRACE_INFO_STRING("BUFFER before trimming:", buffer);
928
929 /* Trucate trailing spaces and CRs */
930 /* Make sure not to step before the beginning */
931 while (len && isspace(buffer[len - 1])) {
932 buffer[len - 1] = '\0';
933 len--;
934 }
935
936 TRACE_INFO_STRING("BUFFER after trimming trailing spaces:", buffer);
937
938 /* Trucate leading spaces */
939 while (isspace(*buffer)) {
940 buffer++;
941 len--;
942 }
943
944 TRACE_INFO_STRING("BUFFER after trimming leading spaces:", buffer);
945 TRACE_INFO_NUMBER("BUFFER length:", len);
946
947 /* Empty line */
948 if (len == 0) {
949 TRACE_FLOW_STRING("Empty line", buf);
950 return RET_EMPTY;
951 }
952
953 /* Section */
954 if (*buffer == '[') {
955 if (buffer[len-1] != ']') {
956 TRACE_ERROR_STRING("Invalid format for section", buf);
957 *ext_error = ERR_NOCLOSESEC;
958 return RET_ERROR;
959 }
960 buffer++;
961 len--;
962 while (isspace(*buffer)) {
963 buffer++;
964 len--;
965 }
966 if (len == 0) {
967 TRACE_ERROR_STRING("Invalid format for section", buf);
968 *ext_error = ERR_NOSECTION;
969 return RET_ERROR;
970 }
971
972 buffer[len - 1] = '\0';
973 len--;
974 while (isspace(buffer[len - 1])) {
975 buffer[len - 1] = '\0';
976 len--;
977 }
978 if (len >= MAX_KEY) {
979 TRACE_ERROR_STRING("Section name is too long", buf);
980 *ext_error = ERR_SECTIONLONG;
981 return RET_ERROR;
982 }
983
984 *key = buffer;
985 return RET_SECTION;
986 }
987
988 /* Assume we are dealing with the K-V here */
989 /* Find "=" */
990 eq = strchr(buffer, '=');
991 if (eq == NULL) {
992 TRACE_ERROR_STRING("No equal sign", buf);
993 *ext_error = ERR_NOEQUAL;
994 return RET_INVALID;
995 }
996
997 len -= eq-buffer;
998
999 /* Strip spaces around "=" */
1000 i = eq - buffer - 1;
1001 while ((i >= 0) && isspace(buffer[i])) i--;
1002 if (i < 0) {
1003 TRACE_ERROR_STRING("No key", buf);
1004 *ext_error = ERR_NOKEY;
1005 return RET_INVALID;
1006 }
1007
1008 /* Copy key into provided buffer */
1009 if(i >= MAX_KEY) {
1010 TRACE_ERROR_STRING("Section name is too long", buf);
1011 *ext_error = ERR_LONGKEY;
1012 return RET_INVALID;
1013 }
1014 *key = buffer;
1015 buffer[i + 1] = '\0';
1016 TRACE_INFO_STRING("KEY:", *key);
1017
1018 eq++;
1019 len--;
1020 while (isspace(*eq)) {
1021 eq++;
1022 len--;
1023 }
1024
1025 *value = eq;
1026 /* Make sure we include trailing 0 into data */
1027 *length = len + 1;
1028
1029 TRACE_INFO_STRING("VALUE:", *value);
1030 TRACE_INFO_NUMBER("LENGTH:", *length);
1031
1032 TRACE_FLOW_STRING("read_line", "Exit");
1033 return RET_PAIR;
1034 }
1035
1036
1037
1038 /* Internal function that prints errors */
1039 static void print_error_list(FILE *file,
1040 struct collection_item *error_list,
1041 int cclass,
1042 char *wrong_col_error,
1043 char *failed_to_process,
1044 char *error_header,
1045 char *line_format,
1046 error_fn error_function)
1047 {
1048 struct collection_iterator *iterator;
1049 int error;
1050 struct collection_item *item = NULL;
1051 struct parse_error *pe;
1052 unsigned int count;
1053
1054 TRACE_FLOW_STRING("print_error_list", "Entry");
1055
1056 /* If we have something to print print it */
1057 if (error_list == NULL) {
1058 TRACE_ERROR_STRING("No error list","");
1059 return;
1060 }
1061
1062 /* Make sure we go the right collection */
1063 if (!col_is_of_class(error_list, cclass)) {
1064 TRACE_ERROR_STRING("Wrong collection class:", wrong_col_error);
1065 fprintf(file,"%s\n", wrong_col_error);
1066 return;
1067 }
1068
1069 /* Bind iterator */
1070 error = col_bind_iterator(&iterator, error_list, COL_TRAVERSE_DEFAULT);
1071 if (error) {
1072 TRACE_ERROR_STRING("Error (bind):", failed_to_process);
1073 fprintf(file, "%s\n", failed_to_process);
1074 return;
1075 }
1076
1077 while(1) {
1078 /* Loop through a collection */
1079 error = col_iterate_collection(iterator, &item);
1080 if (error) {
1081 TRACE_ERROR_STRING("Error (iterate):", failed_to_process);
1082 fprintf(file, "%s\n", failed_to_process);
1083 col_unbind_iterator(iterator);
1084 return;
1085 }
1086
1087 /* Are we done ? */
1088 if (item == NULL) break;
1089
1090 /* Process collection header */
1091 if (col_get_item_type(item) == COL_TYPE_COLLECTION) {
1092 col_get_collection_count(item, &count);
1093 if (count <= 2) break;
1094 } else if (col_get_item_type(item) == COL_TYPE_STRING) {
1095 fprintf(file, error_header, (char *)col_get_item_data(item));
1096 }
1097 else {
1098 /* Put error into provided format */
1099 pe = (struct parse_error *)(col_get_item_data(item));
1100 fprintf(file, line_format,
1101 col_get_item_property(item, NULL), /* Error or warning */
1102 pe->error, /* Error */
1103 pe->line, /* Line */
1104 error_function(pe->error)); /* Error str */
1105 }
1106
1107 }
1108
1109 /* Do not forget to unbind iterator - otherwise there will be a leak */
1110 col_unbind_iterator(iterator);
1111
1112 TRACE_FLOW_STRING("print_error_list", "Exit");
1113 }
1114
1115 /* Print errors and warnings that were detected while parsing one file */
1116 void print_file_parsing_errors(FILE *file,
1117 struct collection_item *error_list)
1118 {
1119 print_error_list(file,
1120 error_list,
1121 COL_CLASS_INI_PERROR,
1122 WRONG_COLLECTION,
1123 FAILED_TO_PROCCESS,
1124 ERROR_HEADER,
1125 LINE_FORMAT,
1126 parsing_error_str);
1127 }
1128
1129
1130 /* Print errors and warnings that were detected while processing grammar */
1131 void print_grammar_errors(FILE *file,
1132 struct collection_item *error_list)
1133 {
1134 print_error_list(file,
1135 error_list,
1136 COL_CLASS_INI_GERROR,
1137 WRONG_GRAMMAR,
1138 FAILED_TO_PROC_G,
1139 ERROR_HEADER_G,
1140 LINE_FORMAT,
1141 grammar_error_str);
1142 }
1143
1144 /* Print errors and warnings that were detected while validating INI file. */
1145 void print_validation_errors(FILE *file,
1146 struct collection_item *error_list)
1147 {
1148 print_error_list(file,
1149 error_list,
1150 COL_CLASS_INI_VERROR,
1151 WRONG_VALIDATION,
1152 FAILED_TO_PROC_V,
1153 ERROR_HEADER_V,
1154 LINE_FORMAT,
1155 validation_error_str);
1156 }
1157
1158 /* Print errors and warnings that were detected while parsing
1159 * the whole configuration */
1160 void print_config_parsing_errors(FILE *file,
1161 struct collection_item *error_list)
1162 {
1163 struct collection_iterator *iterator;
1164 int error;
1165 struct collection_item *item = NULL;
1166 struct collection_item *file_errors = NULL;
1167
1168 TRACE_FLOW_STRING("print_config_parsing_errors", "Entry");
1169
1170 /* If we have something to print print it */
1171 if (error_list == NULL) {
1172 TRACE_ERROR_STRING("No error list", "");
1173 return;
1174 }
1175
1176 /* Make sure we go the right collection */
1177 if (!col_is_of_class(error_list, COL_CLASS_INI_PESET)) {
1178 TRACE_ERROR_STRING("Wrong collection class:", WRONG_COLLECTION);
1179 fprintf(file, "%s\n", WRONG_COLLECTION);
1180 return;
1181 }
1182
1183 /* Bind iterator */
1184 error = col_bind_iterator(&iterator, error_list, COL_TRAVERSE_DEFAULT);
1185 if (error) {
1186 TRACE_ERROR_STRING("Error (bind):", FAILED_TO_PROCCESS);
1187 fprintf(file,"%s\n", FAILED_TO_PROCCESS);
1188 return;
1189 }
1190
1191 while(1) {
1192 /* Loop through a collection */
1193 error = col_iterate_collection(iterator, &item);
1194 if (error) {
1195 TRACE_ERROR_STRING("Error (iterate):", FAILED_TO_PROCCESS);
1196 fprintf(file, "%s\n", FAILED_TO_PROCCESS);
1197 col_unbind_iterator(iterator);
1198 return;
1199 }
1200
1201 /* Are we done ? */
1202 if (item == NULL) break;
1203
1204 /* Print per file sets of errors */
1205 if (col_get_item_type(item) == COL_TYPE_COLLECTIONREF) {
1206 /* Extract a sub collection */
1207 error = col_get_reference_from_item(item, &file_errors);
1208 if (error) {
1209 TRACE_ERROR_STRING("Error (extract):", FAILED_TO_PROCCESS);
1210 fprintf(file, "%s\n", FAILED_TO_PROCCESS);
1211 col_unbind_iterator(iterator);
1212 return;
1213 }
1214 print_file_parsing_errors(file, file_errors);
1215 col_destroy_collection(file_errors);
1216 }
1217 }
1218
1219 /* Do not forget to unbind iterator - otherwise there will be a leak */
1220 col_unbind_iterator(iterator);
1221
1222 TRACE_FLOW_STRING("print_config_parsing_errors", "Exit");
1223 }
1224
1225
1226 /* Function to get value from the configration handle */
1227 int get_config_item(const char *section,
1228 const char *name,
1229 struct collection_item *ini_config,
1230 struct collection_item **item)
1231 {
1232 int error = EOK;
1233 struct collection_item *section_handle = NULL;
1234 const char *to_find;
1235 char default_section[] = INI_DEFAULT_SECTION;
1236
1237 TRACE_FLOW_STRING("get_config_item", "Entry");
1238
1239 /* Do we have the accepting memory ? */
1240 if (item == NULL) {
1241 TRACE_ERROR_NUMBER("No buffer - invalid argument.", EINVAL);
1242 return EINVAL;
1243 }
1244
1245 /* Is the collection of a right type */
1246 if (!col_is_of_class(ini_config, COL_CLASS_INI_CONFIG)) {
1247 TRACE_ERROR_NUMBER("Wrong collection type", EINVAL);
1248 return EINVAL;
1249 }
1250
1251 *item = NULL;
1252
1253 if (section == NULL) to_find = default_section;
1254 else to_find = section;
1255
1256 TRACE_INFO_STRING("Getting Name:", name);
1257 TRACE_INFO_STRING("In Section:", section);
1258
1259 /* Get Subcollection */
1260 error = col_get_collection_reference(ini_config, §ion_handle, to_find);
1261 /* Check error */
1262 if (error && (error != ENOENT)) {
1263 TRACE_ERROR_NUMBER("Failed to get section", error);
1264 return error;
1265 }
1266
1267 /* Did we find a section */
1268 if ((error == ENOENT) || (section_handle == NULL)) {
1269 /* We have not found section - return success */
1270 TRACE_FLOW_STRING("get_value_from_config", "No such section");
1271 return EOK;
1272 }
1273
1274 /* Get item */
1275 error = col_get_item(section_handle, name,
1276 COL_TYPE_STRING, COL_TRAVERSE_ONELEVEL, item);
1277
1278 /* Make sure we free the section we found */
1279 col_destroy_collection(section_handle);
1280
1281 TRACE_FLOW_NUMBER("get_config_item returning", error);
1282 return error;
1283 }
1284
1285 /* Get long long value from config item */
1286 static long long get_llong_config_value(struct collection_item *item,
1287 int strict,
1288 long long def,
1289 int *error)
1290 {
1291 int err;
1292 const char *str;
1293 char *endptr;
1294 long long val = 0;
1295
1296 TRACE_FLOW_STRING("get_long_config_value", "Entry");
1297
1298 /* Do we have the item ? */
1299 if ((item == NULL) ||
1300 (col_get_item_type(item) != COL_TYPE_STRING)) {
1301 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
1302 if (error) *error = EINVAL;
1303 return def;
1304 }
1305
1306 if (error) *error = EOK;
1307
1308 /* Try to parse the value */
1309 str = (const char *)col_get_item_data(item);
1310 errno = 0;
1311 val = strtoll(str, &endptr, 10);
1312 err = errno;
1313
1314 /* Check for various possible errors */
1315 if (err != 0) {
1316 TRACE_ERROR_NUMBER("Conversion failed", err);
1317 if (error) *error = err;
1318 return def;
1319 }
1320
1321 /* Other error cases */
1322 if ((endptr == str) || (strict && (*endptr != '\0'))) {
1323 TRACE_ERROR_NUMBER("More characters or nothing processed", EIO);
1324 if (error) *error = EIO;
1325 return def;
1326 }
1327
1328 TRACE_FLOW_NUMBER("get_long_config_value returning", (long)val);
1329 return val;
1330 }
1331
1332 /* Get unsigned long long value from config item */
1333 static unsigned long long get_ullong_config_value(struct collection_item *item,
1334 int strict,
1335 unsigned long long def,
1336 int *error)
1337 {
1338 int err;
1339 const char *str;
1340 char *endptr;
1341 unsigned long long val = 0;
1342
1343 TRACE_FLOW_STRING("get_long_config_value", "Entry");
1344
1345 /* Do we have the item ? */
1346 if ((item == NULL) ||
1347 (col_get_item_type(item) != COL_TYPE_STRING)) {
1348 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
1349 if (error) *error = EINVAL;
1350 return def;
1351 }
1352
1353 if (error) *error = EOK;
1354
1355 /* Try to parse the value */
1356 str = (const char *)col_get_item_data(item);
1357 errno = 0;
1358 val = strtoull(str, &endptr, 10);
1359 err = errno;
1360
1361 /* Check for various possible errors */
1362 if (err != 0) {
1363 TRACE_ERROR_NUMBER("Conversion failed", err);
1364 if (error) *error = err;
1365 return def;
1366 }
1367
1368 /* Other error cases */
1369 if ((endptr == str) || (strict && (*endptr != '\0'))) {
1370 TRACE_ERROR_NUMBER("More characters or nothing processed", EIO);
1371 if (error) *error = EIO;
1372 return def;
1373 }
1374
1375 TRACE_FLOW_NUMBER("get_long_config_value returning", (long)val);
1376 return val;
1377 }
1378
1379
1380 /* Get integer value from config item */
1381 int get_int_config_value(struct collection_item *item,
1382 int strict,
1383 int def,
1384 int *error)
1385 {
1386 long long val = 0;
1387 int err = 0;
1388
1389 TRACE_FLOW_STRING("get_int_config_value", "Entry");
1390
1391 val = get_llong_config_value(item, strict, def, &err);
1392 if (err == 0) {
1393 if ((val > INT_MAX) || (val < INT_MIN)) {
1394 val = def;
1395 err = ERANGE;
1396 }
1397 }
1398
1399 if (error) *error = err;
1400
1401 TRACE_FLOW_NUMBER("get_int_config_value returning", (int)val);
1402 return (int)val;
1403 }
1404
1405 /* Get unsigned integer value from config item */
1406 unsigned get_unsigned_config_value(struct collection_item *item,
1407 int strict,
1408 unsigned def,
1409 int *error)
1410 {
1411 unsigned long long val = 0;
1412 int err = 0;
1413
1414 TRACE_FLOW_STRING("get_unsigned_config_value", "Entry");
1415
1416 val = get_ullong_config_value(item, strict, def, &err);
1417 if (err == 0) {
1418 if (val > UINT_MAX) {
1419 val = def;
1420 err = ERANGE;
1421 }
1422 }
1423
1424 if (error) *error = err;
1425
1426 TRACE_FLOW_NUMBER("get_unsigned_config_value returning",
1427 (unsigned)val);
1428 return (unsigned)val;
1429 }
1430
1431 /* Get long value from config item */
1432 long get_long_config_value(struct collection_item *item,
1433 int strict,
1434 long def,
1435 int *error)
1436 {
1437 long long val = 0;
1438 int err = 0;
1439
1440 TRACE_FLOW_STRING("get_long_config_value", "Entry");
1441
1442 val = get_llong_config_value(item, strict, def, &err);
1443 if (err == 0) {
1444 if ((val > LONG_MAX) || (val < LONG_MIN)) {
1445 val = def;
1446 err = ERANGE;
1447 }
1448 }
1449
1450 if (error) *error = err;
1451
1452 TRACE_FLOW_NUMBER("get_long_config_value returning",
1453 (long)val);
1454 return (long)val;
1455 }
1456
1457 /* Get unsigned long value from config item */
1458 unsigned long get_ulong_config_value(struct collection_item *item,
1459 int strict,
1460 unsigned long def,
1461 int *error)
1462 {
1463 unsigned long long val = 0;
1464 int err = 0;
1465
1466 TRACE_FLOW_STRING("get_ulong_config_value", "Entry");
1467
1468 val = get_ullong_config_value(item, strict, def, &err);
1469 if (err == 0) {
1470 if (val > ULONG_MAX) {
1471 val = def;
1472 err = ERANGE;
1473 }
1474 }
1475
1476 if (error) *error = err;
1477
1478 TRACE_FLOW_NUMBER("get_ulong_config_value returning",
1479 (unsigned long)val);
1480 return (unsigned long)val;
1481 }
1482
1483
1484 /* Get double value */
1485 double get_double_config_value(struct collection_item *item,
1486 int strict, double def, int *error)
1487 {
1488 const char *str;
1489 char *endptr;
1490 double val = 0;
1491
1492 TRACE_FLOW_STRING("get_double_config_value", "Entry");
1493
1494 /* Do we have the item ? */
1495 if ((item == NULL) ||
1496 (col_get_item_type(item) != COL_TYPE_STRING)) {
1497 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
1498 if (error) *error = EINVAL;
1499 return def;
1500 }
1501
1502 if (error) *error = EOK;
1503
1504 /* Try to parse the value */
1505 str = (const char *)col_get_item_data(item);
1506 errno = 0;
1507 val = strtod(str, &endptr);
1508
1509 /* Check for various possible errors */
1510 if ((errno == ERANGE) ||
1511 ((errno != 0) && (val == 0)) ||
1512 (endptr == str)) {
1513 TRACE_ERROR_NUMBER("Conversion failed", EIO);
1514 if (error) *error = EIO;
1515 return def;
1516 }
1517
1518 if (strict && (*endptr != '\0')) {
1519 TRACE_ERROR_NUMBER("More characters than expected", EIO);
1520 if (error) *error = EIO;
1521 val = def;
1522 }
1523
1524 TRACE_FLOW_NUMBER("get_double_config_value returning", val);
1525 return val;
1526 }
1527
1528 /* Get boolean value */
1529 unsigned char get_bool_config_value(struct collection_item *item,
1530 unsigned char def, int *error)
1531 {
1532 const char *str;
1533 int len;
1534
1535 TRACE_FLOW_STRING("get_bool_config_value", "Entry");
1536
1537 /* Do we have the item ? */
1538 if ((item == NULL) ||
1539 (col_get_item_type(item) != COL_TYPE_STRING)) {
1540 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
1541 if (error) *error = EINVAL;
1542 return def;
1543 }
1544
1545 if (error) *error = EOK;
1546
1547 str = (const char *)col_get_item_data(item);
1548 len = col_get_item_length(item);
1549
1550 /* Try to parse the value */
1551 if ((strncasecmp(str, "true", len) == 0) ||
1552 (strncasecmp(str, "yes", len) == 0)) {
1553 TRACE_FLOW_STRING("Returning", "true");
1554 return '\1';
1555 }
1556 else if ((strncasecmp(str, "false", len) == 0) ||
1557 (strncasecmp(str, "no", len) == 0)) {
1558 TRACE_FLOW_STRING("Returning", "false");
1559 return '\0';
1560 }
1561
1562 TRACE_ERROR_STRING("Returning", "error");
1563 if (error) *error = EIO;
1564 return def;
1565 }
1566
1567 /* Return a string out of the value */
1568 char *get_string_config_value(struct collection_item *item,
1569 int *error)
1570 {
1571 char *str = NULL;
1572
1573 TRACE_FLOW_STRING("get_string_config_value", "Entry");
1574
1575 /* Do we have the item ? */
1576 if ((item == NULL) ||
1577 (col_get_item_type(item) != COL_TYPE_STRING)) {
1578 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
1579 if (error) *error = EINVAL;
1580 return NULL;
1581 }
1582
1583 str = strdup((const char *)col_get_item_data(item));
1584 if (str == NULL) {
1585 TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
1586 if (error) *error = ENOMEM;
1587 return NULL;
1588 }
1589
1590 if (error) *error = EOK;
1591
1592 TRACE_FLOW_STRING("get_string_config_value returning", str);
1593 return str;
1594 }
1595
1596 /* Get string from item */
1597 const char *get_const_string_config_value(struct collection_item *item, int *error)
1598 {
1599 const char *str;
1600
1601 TRACE_FLOW_STRING("get_const_string_config_value", "Entry");
1602
1603 /* Do we have the item ? */
1604 if ((item == NULL) ||
1605 (col_get_item_type(item) != COL_TYPE_STRING)) {
1606 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
1607 if (error) *error = EINVAL;
1608 return NULL;
1609 }
1610
1611 str = (const char *)col_get_item_data(item);
1612
1613 if (error) *error = EOK;
1614
1615 TRACE_FLOW_STRING("get_const_string_config_value returning", str);
1616 return str;
1617 }
1618
1619 /* A special hex format is assumed.
1620 * The string should be taken in single quotes
1621 * and consist of hex encoded value two hex digits per byte.
1622 * Example: '0A2BFECC'
1623 * Case does not matter.
1624 */
1625 char *get_bin_config_value(struct collection_item *item,
1626 int *length, int *error)
1627 {
1628 int i;
1629 char *value = NULL;
1630 const char *buff;
1631 int size = 0;
1632 unsigned char hex;
1633 int len;
1634 const char *str;
1635
1636 TRACE_FLOW_STRING("get_bin_config_value", "Entry");
1637
1638 /* Do we have the item ? */
1639 if ((item == NULL) ||
1640 (col_get_item_type(item) != COL_TYPE_STRING)) {
1641 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
1642 if (error) *error = EINVAL;
1643 return NULL;
1644 }
1645
1646 /* Check the length */
1647 len = col_get_item_length(item)-1;
1648 if ((len%2) != 0) {
1649 TRACE_ERROR_STRING("Invalid length for binary data", "");
1650 if (error) *error = EINVAL;
1651 return NULL;
1652 }
1653
1654 str = (const char *)col_get_item_data(item);
1655
1656 /* Is the format correct ? */
1657 if ((*str != '\'') ||
1658 (str[len -1] != '\'')) {
1659 TRACE_ERROR_STRING("String is not escaped","");
1660 if (error) *error = EIO;
1661 return NULL;
1662 }
1663
1664 /* Check that all the symbols are ok */
1665 buff = str + 1;
1666 len -= 2;
1667 for (i = 0; i < len; i += 2) {
1668 if (!isxdigit(buff[i]) || !isxdigit(buff[i + 1])) {
1669 TRACE_ERROR_STRING("Invalid encoding for binary data", buff + i);
1670 if (error) *error = EIO;
1671 return NULL;
1672 }
1673 }
1674
1675 /* The value is good so we can allocate memory for it */
1676 value = malloc(len / 2);
1677 if (value == NULL) {
1678 TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
1679 if (error) *error = ENOMEM;
1680 return NULL;
1681 }
1682
1683 /* Convert the value */
1684 for (i = 0; i < len; i += 2) {
1685 if (isdigit(buff[i])) {
1686 if (isdigit(buff[i+1]))
1687 hex = 16 * (buff[i] - '0') + (buff[i+1] - '0');
1688 else
1689 hex = 16 * (buff[i] - '0') + (tolower(buff[i+1]) - 'a' + 10);
1690 }
1691 else {
1692 if (isdigit(buff[i+1]))
1693 hex = 16 * (tolower(buff[i]) - 'a') + (buff[i+1] - '0');
1694 else
1695 hex = 16 * (tolower(buff[i]) - 'a' + 10) + (tolower(buff[i+1]) - 'a' + 10);
1696 }
1697
1698 value[size] = (char)(hex);
1699 size++;
1700 }
1701
1702 if (error) *error = EOK;
1703 if (length) *length = size;
1704 TRACE_FLOW_STRING("get_bin_config_value", "Exit");
1705 return value;
1706 }
1707
1708 /* Function to free binary configuration value */
1709 void free_bin_config_value(char *value)
1710 {
1711 if (value) free(value);
1712 }
1713
1714 /* Arrays of stings */
1715 static char **get_str_cfg_array(struct collection_item *item,
1716 int include,
1717 const char *sep,
1718 int *size,
1719 int *error)
1720 {
1721 char *copy = NULL;
1722 char *dest = NULL;
1723 char locsep[4];
1724 int lensep;
1725 char *buff;
1726 int count = 0;
1727 int len = 0;
1728 int resume_len;
1729 char **array;
1730 char *start;
1731 int i, j;
1732 int dlen;
1733
1734 TRACE_FLOW_STRING("get_str_cfg_array", "Entry");
1735
1736 /* Do we have the item ? */
1737 if ((item == NULL) ||
1738 (col_get_item_type(item) != COL_TYPE_STRING)) {
1739 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
1740 if (error) *error = EINVAL;
1741 return NULL;
1742 }
1743
1744 /* Handle the separators */
1745 if (sep == NULL) {
1746 locsep[0] = ',';
1747 locsep[1] = '\0';
1748 lensep = 2;
1749 }
1750 else {
1751 strncpy(locsep, sep, 3);
1752 locsep[3] = '\0';
1753 lensep = strlen(locsep) + 1;
1754 }
1755
1756 /* Allocate memory for the copy of the string */
1757 copy = malloc(col_get_item_length(item));
At conditional (1): "copy == NULL": Taking false branch.
|
1758 if (copy == NULL) {
1759 TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
1760 if (error) *error = ENOMEM;
1761 return NULL;
1762 }
1763
1764 /* Loop through the string */
1765 dest = copy;
1766 buff = col_get_item_data(item);
1767 start = buff;
1768 dlen = col_get_item_length(item);
At conditional (2): "i < dlen": Taking true branch.
At conditional (9): "i < dlen": Taking true branch.
At conditional (26): "i < dlen": Taking true branch.
At conditional (33): "i < dlen": Taking false branch.
| | | |
1769 for(i = 0; i < dlen; i++) {
At conditional (3): "j < lensep": Taking true branch.
At conditional (10): "j < lensep": Taking true branch.
At conditional (27): "j < lensep": Taking true branch.
| | |
1770 for(j = 0; j < lensep; j++) {
At conditional (4): "buff[i] == locsep[j]": Taking true branch.
At conditional (11): "buff[i] == locsep[j]": Taking true branch.
At conditional (28): "buff[i] == locsep[j]": Taking true branch.
| | |
1771 if(buff[i] == locsep[j]) {
1772 /* If we found one of the separators trim spaces around */
1773 resume_len = len;
At conditional (5): "len > 0": Taking false branch.
At conditional (12): "len > 0": Taking true branch.
At conditional (14): "len > 0": Taking false branch.
At conditional (29): "len > 0": Taking false branch.
| | | |
1774 while (len > 0) {
At conditional (13): "*__ctype_b_loc()[(int)start[len - 1]] & 0x2000": Taking true branch.
|
1775 if (isspace(start[len - 1])) len--;
1776 else break;
1777 }
1778 TRACE_INFO_STRING("Current:", start);
1779 TRACE_INFO_NUMBER("Length:", len);
At conditional (6): "len > 0": Taking false branch.
At conditional (15): "len > 0": Taking false branch.
At conditional (30): "len > 0": Taking false branch.
| | |
1780 if (len > 0) {
1781 /* Save block aside */
1782 memcpy(dest, start, len);
1783 count++;
1784 dest += len;
1785 *dest = '\0';
1786 dest++;
1787 }
At conditional (7): "include": Taking true branch.
At conditional (16): "include": Taking true branch.
At conditional (31): "include": Taking true branch.
| | |
1788 else if(include) {
1789 count++;
1790 *dest = '\0';
1791 dest++;
1792 }
At conditional (8): "locsep[j] == 0": Taking true branch.
At conditional (17): "locsep[j] == 0": Taking false branch.
At conditional (32): "locsep[j] == 0": Taking true branch.
| | |
1793 if (locsep[j] == '\0') break; /* We are done */
1794
1795 /* Move forward and trim spaces if any */
1796 start += resume_len + 1;
1797 i++;
1798 TRACE_INFO_STRING("Other pointer :", buff + i);
At conditional (18): "i < dlen": Taking true branch.
At conditional (19): "*__ctype_b_loc()[(int)*start] & 0x2000": Taking true branch.
At conditional (20): "i < dlen": Taking true branch.
At conditional (21): "*__ctype_b_loc()[(int)*start] & 0x2000": Taking true branch.
At conditional (22): "i < dlen": Taking true branch.
At conditional (23): "*__ctype_b_loc()[(int)*start] & 0x2000": Taking true branch.
At conditional (24): "i < dlen": Taking true branch.
At conditional (25): "*__ctype_b_loc()[(int)*start] & 0x2000": Taking false branch.
| | | | | | | |
1799 while ((i < dlen) && (isspace(*start))) {
1800 i++;
1801 start++;
1802 }
1803 len = -1; /* Len will be increased in the loop */
1804 i--; /* i will be increas so we need to step back */
1805 TRACE_INFO_STRING("Remaining buffer after triming spaces:", start);
1806 break;
1807 }
1808 }
1809 len++;
1810 }
1811
1812 /* Now we know how many items are there in the list */
1813 array = malloc((count + 1) * sizeof(char *));
At conditional (34): "array == NULL": Taking false branch.
|
1814 if (array == NULL) {
1815 free(copy);
1816 TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
1817 if (error) *error = ENOMEM;
1818 return NULL;
1819 }
1820
1821 /* Loop again to fill in the pointers */
1822 start = copy;
At conditional (35): "i < count": Taking false branch.
|
1823 for (i = 0; i < count; i++) {
1824 TRACE_INFO_STRING("Token :", start);
1825 TRACE_INFO_NUMBER("Item :", i);
1826 array[i] = start;
1827 /* Move to next item */
1828 while(*start) start++;
1829 start++;
1830 }
1831 array[count] = NULL;
1832
At conditional (36): "error": Taking true branch.
|
1833 if (error) *error = EOK;
At conditional (37): "size": Taking true branch.
|
1834 if (size) *size = count;
1835 TRACE_FLOW_STRING("get_str_cfg_array", "Exit");
Event leaked_storage: Variable "copy" going out of scope leaks the storage it points to. Event leaked_storage: Variable "dest" going out of scope leaks the storage it points to. Event leaked_storage: Variable "start" going out of scope leaks the storage it points to.
Also see events: [alloc_fn][var_assign][var_assign][var_assign] | |
1836 return array;
1837 }
1838
1839 /* Get array of strings from item eliminating empty tokens */
1840 char **get_string_config_array(struct collection_item *item,
1841 const char *sep, int *size, int *error)
1842 {
1843 TRACE_FLOW_STRING("get_string_config_array", "Called.");
1844 return get_str_cfg_array(item, EXCLUDE_EMPTY, sep, size, error);
1845 }
1846 /* Get array of strings from item preserving empty tokens */
1847 char **get_raw_string_config_array(struct collection_item *item,
1848 const char *sep, int *size, int *error)
1849 {
1850 TRACE_FLOW_STRING("get_raw_string_config_array", "Called.");
1851 return get_str_cfg_array(item, INCLUDE_EMPTY, sep, size, error);
1852 }
1853
1854 /* Special function to free string config array */
1855 void free_string_config_array(char **str_config)
1856 {
1857 TRACE_FLOW_STRING("free_string_config_array", "Entry");
1858
1859 if (str_config != NULL) {
1860 if (*str_config != NULL) free(*str_config);
1861 free(str_config);
1862 }
1863
1864 TRACE_FLOW_STRING("free_string_config_array", "Exit");
1865 }
1866
1867 /* Get an array of long values.
1868 * NOTE: For now I leave just one function that returns numeric arrays.
1869 * In future if we need other numeric types we can change it to do strtoll
1870 * internally and wrap it for backward compatibility.
1871 */
1872 long *get_long_config_array(struct collection_item *item, int *size, int *error)
1873 {
1874 const char *str;
1875 char *endptr;
1876 long val = 0;
1877 long *array;
1878 int count = 0;
1879 int err;
1880
1881 TRACE_FLOW_STRING("get_long_config_array", "Entry");
1882
1883 /* Do we have the item ? */
1884 if ((item == NULL) ||
1885 (col_get_item_type(item) != COL_TYPE_STRING) ||
1886 (size == NULL)) {
1887 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
1888 if (error) *error = EINVAL;
1889 return NULL;
1890 }
1891
1892 /* Assume that we have maximum number of different numbers */
1893 array = (long *)malloc(sizeof(long) * col_get_item_length(item)/2);
1894 if (array == NULL) {
1895 TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
1896 if (error) *error = ENOMEM;
1897 return NULL;
1898 }
1899
1900 /* Now parse the string */
1901 str = (const char *)col_get_item_data(item);
1902 while (*str) {
1903
1904 errno = 0;
1905 val = strtol(str, &endptr, 10);
1906 err = errno;
1907
1908 if (err) {
1909 TRACE_ERROR_NUMBER("Conversion failed", err);
1910 free(array);
1911 if (error) *error = err;
1912 return NULL;
1913 }
1914
1915 if (endptr == str) {
1916 TRACE_ERROR_NUMBER("Nothing processed", EIO);
1917 free(array);
1918 if (error) *error = EIO;
1919 return NULL;
1920 }
1921
1922 /* Save value */
1923 array[count] = val;
1924 count++;
1925 /* Are we done? */
1926 if (*endptr == 0) break;
1927 /* Advance to the next valid number */
1928 for (str = endptr; *str; str++) {
1929 if (isdigit(*str) || (*str == '-') || (*str == '+')) break;
1930 }
1931 }
1932
1933 *size = count;
1934 if (error) *error = EOK;
1935
1936 TRACE_FLOW_NUMBER("get_long_config_value returning", val);
1937 return array;
1938
1939 }
1940
1941 /* Get an array of double values */
1942 double *get_double_config_array(struct collection_item *item, int *size, int *error)
1943 {
1944 const char *str;
1945 char *endptr;
1946 double val = 0;
1947 double *array;
1948 int count = 0;
1949 struct lconv *loc;
1950
1951 TRACE_FLOW_STRING("get_double_config_array", "Entry");
1952
1953 /* Do we have the item ? */
1954 if ((item == NULL) ||
1955 (col_get_item_type(item) != COL_TYPE_STRING) ||
1956 (size == NULL)) {
1957 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
1958 if (error) *error = EINVAL;
1959 return NULL;
1960 }
1961
1962 /* Assume that we have maximum number of different numbers */
1963 array = (double *)malloc(sizeof(double) * col_get_item_length(item)/2);
1964 if (array == NULL) {
1965 TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
1966 if (error) *error = ENOMEM;
1967 return NULL;
1968 }
1969
1970 /* Get locale information so that we can check for decimal point character.
1971 * Based on the man pages it is unclear if this is an allocated memory or not.
1972 * Seems like it is a static thread or process local structure so
1973 * I will not try to free it after use.
1974 */
1975 loc = localeconv();
1976
1977 /* Now parse the string */
1978 str = (const char *)col_get_item_data(item);
1979 while (*str) {
1980 TRACE_INFO_STRING("String to convert",str);
1981 errno = 0;
1982 val = strtod(str, &endptr);
1983 if ((errno == ERANGE) ||
1984 ((errno != 0) && (val == 0)) ||
1985 (endptr == str)) {
1986 TRACE_ERROR_NUMBER("Conversion failed", EIO);
1987 free(array);
1988 if (error) *error = EIO;
1989 return NULL;
1990 }
1991 /* Save value */
1992 array[count] = val;
1993 count++;
1994 /* Are we done? */
1995 if (*endptr == 0) break;
1996 TRACE_INFO_STRING("End pointer after conversion",endptr);
1997 /* Advance to the next valid number */
1998 for (str = endptr; *str; str++) {
1999 if (isdigit(*str) || (*str == '-') || (*str == '+') ||
2000 /* It is ok to do this since the string is null terminated */
2001 ((*str == *(loc->decimal_point)) && isdigit(str[1]))) break;
2002 }
2003 }
2004
2005 *size = count;
2006 if (error) *error = EOK;
2007
2008 TRACE_FLOW_NUMBER("get_double_config_value returning", val);
2009 return array;
2010
2011 }
2012
2013
2014 /* Special function to free long config array */
2015 void free_long_config_array(long *array)
2016 {
2017 if (array != NULL) free(array);
2018 }
2019
2020 /* Special function to free double config array */
2021 void free_double_config_array(double *array)
2022 {
2023 if (array != NULL) free(array);
2024 }
2025
2026 /* The section array should be freed using this function */
2027 void free_section_list(char **section_list)
2028 {
2029 TRACE_FLOW_STRING("free_section_list","Entry");
2030
2031 col_free_property_list(section_list);
2032
2033 TRACE_FLOW_STRING("free_section_list","Exit");
2034 }
2035
2036 /* The section array should be freed using this function */
2037 void free_attribute_list(char **section_list)
2038 {
2039 TRACE_FLOW_STRING("free_section_list","Entry");
2040
2041 col_free_property_list(section_list);
2042
2043 TRACE_FLOW_STRING("free_section_list","Exit");
2044 }
2045
2046
2047 /* Get list of sections as an array of strings.
2048 * Function allocates memory for the array of the sections.
2049 */
2050 char **get_section_list(struct collection_item *ini_config, int *size, int *error)
2051 {
2052 char **list;
2053
2054 TRACE_FLOW_STRING("get_section_list","Entry");
2055 /* Do we have the item ? */
2056 if ((ini_config == NULL) ||
2057 !col_is_of_class(ini_config, COL_CLASS_INI_CONFIG)) {
2058 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
2059 if (error) *error = EINVAL;
2060 return NULL;
2061 }
2062
2063 /* Pass it to the function from collection API */
2064 list = col_collection_to_list(ini_config, size, error);
2065
2066 TRACE_FLOW_STRING("get_section_list returning", ((list == NULL) ? "NULL" : list[0]));
2067 return list;
2068 }
2069
2070 /* Get list of attributes in a section as an array of strings.
2071 * Function allocates memory for the array of the strings.
2072 */
2073 char **get_attribute_list(struct collection_item *ini_config, const char *section, int *size, int *error)
2074 {
2075 struct collection_item *subcollection = NULL;
2076 char **list;
2077 int err;
2078
2079 TRACE_FLOW_STRING("get_attribute_list","Entry");
2080 /* Do we have the item ? */
2081 if ((ini_config == NULL) ||
2082 !col_is_of_class(ini_config, COL_CLASS_INI_CONFIG) ||
2083 (section == NULL)) {
2084 TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
2085 if (error) *error = EINVAL;
2086 return NULL;
2087 }
2088
2089 /* Fetch section */
2090 err = col_get_collection_reference(ini_config, &subcollection, section);
2091 /* Check error */
2092 if (err && (subcollection == NULL)) {
2093 TRACE_ERROR_NUMBER("Failed to get section", err);
2094 if (error) *error = EINVAL;
2095 return NULL;
2096 }
2097
2098 /* Pass it to the function from collection API */
2099 list = col_collection_to_list(subcollection, size, error);
2100
2101 col_destroy_collection(subcollection);
2102
2103 TRACE_FLOW_STRING("get_attribute_list returning", ((list == NULL) ? "NULL" : list[0]));
2104 return list;
2105 }