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, &current_section,
265  	                                                     INI_DEFAULT_SECTION);
266  	                if (error != EOK) {
267  	                    /* Create default collection */
268  	                    if ((error = col_create_collection(&current_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, &current_section, key);
337  	            if (error != EOK) {
338  	                /* Create default collection */
339  	                if ((error = col_create_collection(&current_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, &section_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 */
Event alloc_fn: Calling allocation function "malloc".
Event var_assign: Assigning: "copy" = storage returned from "malloc(col_get_item_length(item))".
Also see events: [var_assign][var_assign][leaked_storage][leaked_storage][leaked_storage]
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 */
Event var_assign: Assigning: "dest" = "copy".
Also see events: [alloc_fn][var_assign][var_assign][leaked_storage][leaked_storage][leaked_storage]
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 */
Event var_assign: Assigning: "start" = "copy".
Also see events: [alloc_fn][var_assign][var_assign][leaked_storage][leaked_storage][leaked_storage]
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 	}