summaryrefslogtreecommitdiff
path: root/common/ini/ini_config.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/ini/ini_config.c')
-rw-r--r--common/ini/ini_config.c1286
1 files changed, 1286 insertions, 0 deletions
diff --git a/common/ini/ini_config.c b/common/ini/ini_config.c
new file mode 100644
index 00000000..cf70c9c8
--- /dev/null
+++ b/common/ini/ini_config.c
@@ -0,0 +1,1286 @@
+/*
+ INI LIBRARY
+
+ Reading configuration from INI file
+ and storing as a collection.
+
+ Copyright (C) Dmitri Pal <dpal@redhat.com> 2009
+
+ INI Library is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ INI Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with INI Library. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include "config.h"
+/* For error text */
+#include <libintl.h>
+#define _(String) gettext (String)
+/* INI file is used as a collection */
+#include "collection_priv.h"
+#include "collection.h"
+#include "collection_tools.h"
+#include "trace.h"
+#include "ini_config.h"
+
+#define NAME_OVERHEAD 10
+
+#define SLASH "/"
+
+/* Name of the special collection used to store parsing errors */
+#define FILE_ERROR_SET "ini_file_error_set"
+
+/* Text error strings used when errors are printed out */
+#define WARNING_TXT _("Warning")
+#define ERROR_TXT _("Error")
+#define WRONG_COLLECTION _("Passed in list is not a list of parse errors.\n")
+#define FAILED_TO_PROCCESS _("Internal Error. Failed to process error list.\n")
+#define ERROR_HEADER _("Parsing errors and warnings in file: %s\n")
+#define LINE_FORMAT _("%s (%d) on line %d: %s\n")
+
+/* Codes that parsing function can return */
+#define RET_PAIR 0
+#define RET_COMMENT 1
+#define RET_SECTION 2
+#define RET_INVALID 3
+#define RET_EMPTY 4
+#define RET_EOF 5
+#define RET_ERROR 6
+
+/* STATIC INTERNAL FUNCTIONS */
+#ifdef HAVE_PARSE_ERROR
+
+
+/* Function to return parsing error */
+inline const char *parsing_error_str(int parsing_error)
+{
+ char *placeholder= _("Unknown error.");
+ char *str_error[] = { _("Data is too long."),
+ _("No closing bracket."),
+ _("Section name is missing."),
+ _("Section name is too long."),
+ _("Equal sign is missing."),
+ _("Property name is missing."),
+ _("Property name is too long.")
+ };
+
+ /* Check the range */
+ if((parsing_error < 1) || (parsing_error > ERR_MAXPARSE)) return (const char *)placeholder;
+ else return (const char *)(str_error[parsing_error-1]);
+}
+
+#else
+
+
+inline const char *parsing_error_str(int parsing_error)
+{
+ char *placeholder= _("Parsing errors are not compiled.");
+ return (const char*)(placeholder);
+}
+
+#endif
+
+/* Add to collection or update - CONSIDER moving to the collection.c */
+static int add_or_update(struct collection_item *current_section,
+ char *key,
+ void *value,
+ int length,
+ int type)
+{
+ int found = COL_NOMATCH;
+ int error = EOK;
+
+ TRACE_FLOW_STRING("add_or_update", "Entry");
+
+ (void)is_item_in_collection(current_section,key,COL_TYPE_ANY,COL_TRAVERSE_IGNORE,&found);
+
+ if(found == COL_MATCH) {
+ TRACE_INFO_STRING("Updating...", "");
+ error = update_property(current_section,key,type,value,length,COL_TRAVERSE_IGNORE);
+ }
+ else {
+ TRACE_INFO_STRING("Adding...", "");
+ error = add_any_property(current_section,NULL,key,type,value,length);
+ }
+
+ TRACE_FLOW_NUMBER("add_or_update returning", error);
+ return error;
+}
+
+/***************************************************************************/
+/* Function to read single ini file and pupulate
+ * the provided collection with subcollcetions from the file */
+static int ini_to_collection(const char *filename,
+ struct collection_item *ini_config,
+ int error_level,
+ struct collection_item **error_list)
+{
+ FILE *file;
+ int error;
+ int status;
+ int section_count = 0;
+ char *key = NULL;
+ char *value = NULL;
+ struct collection_item *current_section = (struct collection_item *)(NULL);
+ int length;
+ int type;
+ int ext_err = -1;
+ struct parse_error pe;
+ int line = 0;
+ int created = 0;
+
+ TRACE_FLOW_STRING("ini_to_collection", "Entry");
+
+ /* Open file for reading */
+ file = fopen(filename,"r");
+ if(file == NULL) {
+ error = errno;
+ TRACE_ERROR_NUMBER("Failed to open file - but this is OK", error);
+ return EOK;
+ }
+
+ /* Open the collection of errors */
+ if(error_list != (struct collection_item **)(NULL)) {
+ *error_list = (struct collection_item *)(NULL);
+ error = create_collection(error_list,(char *)filename,COL_CLASS_INI_PERROR);
+ if(error) {
+ TRACE_ERROR_NUMBER("Failed to create error collection", error);
+ fclose(file);
+ return EOK;
+ }
+ created = 1;
+ }
+
+ /* Read file lines */
+ while((status = read_line(file,&key,&value,&length,&ext_err)) != RET_EOF) {
+
+ line++;
+
+ switch(status) {
+ case RET_PAIR:
+ /* Do we have a section at the top of the file ? */
+ if(section_count == 0) {
+ /* Check if collection already exists */
+ error = get_collection_reference(ini_config,&current_section,INI_DEFAULT_SECTION);
+ if(error != EOK) {
+ /* Create default collection */
+ if((error=create_collection(&current_section,INI_DEFAULT_SECTION,COL_CLASS_INI_SECTION)) ||
+ (error=add_collection_to_collection(ini_config,NULL,NULL,
+ current_section,
+ COL_ADD_MODE_REFERENCE))) {
+ TRACE_ERROR_NUMBER("Failed to create collection", error);
+ fclose(file);
+ destroy_collection(current_section);
+ if(created) destroy_collection(*error_list);
+ return error;
+ }
+ }
+ section_count++;
+ }
+
+ /* Put value into the collection */
+ if((error=add_or_update(current_section,key,value,length,COL_TYPE_STRING))) {
+ TRACE_ERROR_NUMBER("Failed to add pair to collection", error);
+ fclose(file);
+ destroy_collection(current_section);
+ if(created) destroy_collection(*error_list);
+ return error;
+ }
+ break;
+
+ case RET_SECTION:
+ /* Read a new section */
+ destroy_collection(current_section);
+ current_section = (struct collection_item *)(NULL);
+
+ error = get_collection_reference(ini_config,&current_section,key);
+ if(error != EOK) {
+ /* Create default collection */
+ if((error=create_collection(&current_section,key,COL_CLASS_INI_SECTION)) ||
+ (error=add_collection_to_collection(ini_config,NULL,NULL,
+ current_section,
+ COL_ADD_MODE_REFERENCE))) {
+ TRACE_ERROR_NUMBER("Failed to add collection", error);
+ fclose(file);
+ destroy_collection(current_section);
+ if(created) destroy_collection(*error_list);
+ return error;
+ }
+ }
+ section_count++;
+ break;
+
+ case RET_EMPTY:
+ TRACE_INFO_STRING("Empty string", "");
+ break;
+
+ case RET_COMMENT:
+ TRACE_INFO_STRING("Comment", "");
+ break;
+
+ case RET_ERROR:
+ pe.line = line;
+ pe.error = ext_err;
+ error = add_binary_property(*error_list,NULL, ERROR_TXT,(void *)&pe,sizeof(pe));
+ if(error) {
+ TRACE_ERROR_NUMBER("Failed to add error to collection", error);
+ fclose(file);
+ destroy_collection(current_section);
+ if(created) destroy_collection(*error_list);
+ return error;
+ }
+ /* Exit if there was an error parsing file */
+ if(error_level != INI_STOP_ON_NONE) {
+ TRACE_ERROR_STRING("Invalid format of the file", "");
+ destroy_collection(current_section);
+ fclose(file);
+ return EIO;
+ }
+ break;
+ case RET_INVALID:
+ default:
+ pe.line = line;
+ pe.error = ext_err;
+ error = add_binary_property(*error_list,NULL, WARNING_TXT,(void *)&pe,sizeof(pe));
+ if(error) {
+ TRACE_ERROR_NUMBER("Failed to add warning to collection", error);
+ fclose(file);
+ destroy_collection(current_section);
+ if(created) destroy_collection(*error_list);
+ return error;
+ }
+ /* Exit if we are told to exit on warnings */
+ if(error_level == INI_STOP_ON_ANY) {
+ TRACE_ERROR_STRING("Invalid format of the file", "");
+ if(created) destroy_collection(current_section);
+ fclose(file);
+ return EIO;
+ }
+ TRACE_ERROR_STRING("Invalid string", "");
+ break;
+ }
+ ext_err = -1;
+ }
+
+ /* Close file */
+ fclose(file);
+
+ DEBUG_COLLECTION(ini_config);
+
+ destroy_collection(current_section);
+
+ DEBUG_COLLECTION(ini_config);
+
+ TRACE_FLOW_STRING("ini_to_collection", "Success Exit");
+
+ return EOK;
+}
+
+/*********************************************************************/
+/* Read configuration information from a file */
+int config_from_file(const char *application,
+ const char *config_file,
+ struct collection_item **ini_config,
+ int error_level,
+ struct collection_item **error_list)
+{
+ int error;
+ int created = 0;
+
+ TRACE_FLOW_STRING("config_from_file", "Entry");
+
+ if((ini_config == (struct collection_item **)(NULL)) ||
+ (application == NULL)) {
+ TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
+ return EINVAL;
+ }
+
+ /* Create collection if needed */
+ if(*ini_config == (struct collection_item *)(NULL)) {
+ if((error=create_collection(ini_config,(char *)application,COL_CLASS_INI_CONFIG))) {
+ TRACE_ERROR_NUMBER("Failed to create collection", error);
+ return error;
+ }
+ created = 1;
+ }
+ /* Is the collection of the right class? */
+ else if(is_of_class(*ini_config,COL_CLASS_INI_CONFIG)) {
+ TRACE_ERROR_NUMBER("Wrong collection type", EINVAL);
+ return EINVAL;
+ }
+
+ /* Do the actual work */
+ error = ini_to_collection((char *)config_file, *ini_config,error_level, error_list);
+ /* In case of error when we created collection - delete it */
+ if((error) && (created)) {
+ destroy_collection(*ini_config);
+ *ini_config = (struct collection_item *)(NULL);
+ }
+
+ TRACE_FLOW_NUMBER("config_from_file. Returns", error);
+ return error;
+}
+
+/* Read default config file and then overwrite it with a specific one from the directory */
+int config_for_app(const char *application,
+ const char *config_file,
+ const char *config_dir,
+ struct collection_item **ini_config,
+ int error_level,
+ struct collection_item **error_set)
+{
+ int error=EOK;
+ char *file_name;
+ struct collection_item *error_list_common = (struct collection_item *)(NULL);
+ struct collection_item *error_list_specific = (struct collection_item *)(NULL);
+ struct collection_item **pass_common = (struct collection_item **)(NULL);
+ struct collection_item **pass_specific = (struct collection_item **)(NULL);
+ struct collection_item *error_file_set = (struct collection_item *)(NULL);
+ int created = 0;
+
+ TRACE_FLOW_STRING("config_to_collection", "Entry");
+
+ if(ini_config == (struct collection_item **)(NULL)) {
+ TRACE_ERROR_NUMBER("Failed to create collection", EINVAL);
+ return EINVAL;
+ }
+
+ /* Prepare error collection pointers */
+ if(error_set != (struct collection_item **)(NULL)) {
+ TRACE_INFO_STRING("Error set is not NULL", "preparing error set");
+ pass_common = &error_list_common;
+ pass_specific = &error_list_specific;
+ *error_set = (struct collection_item *)(NULL);
+ /* Construct the overarching error collection */
+ if((error=create_collection(error_set,FILE_ERROR_SET,COL_CLASS_INI_PESET))) {
+ TRACE_ERROR_NUMBER("Failed to create collection", error);
+ return error;
+ }
+ }
+ else {
+ TRACE_INFO_STRING("No error set. Errors will not be captured", "");
+ pass_common = (struct collection_item **)(NULL);
+ pass_specific = (struct collection_item **)(NULL);
+ }
+
+ /* Create collection if needed */
+ if(*ini_config == (struct collection_item *)(NULL)) {
+ TRACE_INFO_STRING("New config collection. Allocate.", "");
+ if((error=create_collection(ini_config,(char *)application,COL_CLASS_INI_CONFIG))) {
+ TRACE_ERROR_NUMBER("Failed to create collection", error);
+ destroy_collection(*error_set);
+ *error_set = (struct collection_item *)(NULL);
+ return error;
+ }
+ }
+ /* Is the collection of the right class? */
+ else if(is_of_class(*ini_config,COL_CLASS_INI_CONFIG)) {
+ TRACE_ERROR_NUMBER("Wrong collection type", EINVAL);
+ return EINVAL;
+ }
+
+ /* Read master file */
+ if(config_file != NULL) {
+ TRACE_INFO_STRING("Reading master file:", config_file);
+ if((error = ini_to_collection(config_file,*ini_config, error_level, pass_common))) {
+ TRACE_ERROR_NUMBER("Failed to read master file", error);
+ /* In case of error when we created collection - delete it */
+ if((error) && (created)) {
+ destroy_collection(*ini_config);
+ *ini_config = (struct collection_item *)(NULL);
+ }
+ /* We do not clear the error_set here */
+ return error;
+ }
+ /* Add error results if any to the overarching error collection */
+ if((pass_common != (struct collection_item **)(NULL)) &&
+ (*pass_common != (struct collection_item *)(NULL))) {
+ TRACE_INFO_STRING("Process erros resulting from file:", config_file);
+ error = add_collection_to_collection(*error_set,NULL,NULL,*pass_common,COL_ADD_MODE_EMBED);
+ if(error) {
+ if(created) {
+ destroy_collection(*ini_config);
+ *ini_config = (struct collection_item *)(NULL);
+ }
+ destroy_collection(*error_set);
+ *error_set = (struct collection_item *)(NULL);
+ TRACE_ERROR_NUMBER("Failed to add error collection to another error collection", error);
+ return error;
+ }
+ }
+ }
+
+ if(config_dir != NULL) {
+ /* Get specific application file */
+ file_name = malloc(strlen(config_dir) + strlen(application) + NAME_OVERHEAD);
+ if(file_name == NULL) {
+ error = errno;
+ TRACE_ERROR_NUMBER("Failed to allocate memory for file name", error);
+ /* In case of error when we created collection - delete it */
+ if((error) && (created)) {
+ destroy_collection(*ini_config);
+ *ini_config = (struct collection_item *)(NULL);
+ }
+ destroy_collection(*error_set);
+ *error_set = (struct collection_item *)(NULL);
+ return error;
+ }
+
+ sprintf(file_name,"%s%s%s.conf",config_dir, SLASH, application);
+ TRACE_INFO_STRING("Opening file:", file_name);
+
+ /* Read master file */
+ error = ini_to_collection(file_name,*ini_config,error_level,pass_specific);
+ free(file_name);
+ if(error) {
+ TRACE_ERROR_NUMBER("Failed to read specific application file", error);
+ /* In case of error when we created collection - delete it */
+ if((error) && (created)) {
+ destroy_collection(*ini_config);
+ *ini_config = (struct collection_item *)(NULL);
+ }
+ /* We do not clear the error_set here */
+ return error;
+ }
+
+ /* Add error results if any to the overarching error collection */
+ if((pass_specific != (struct collection_item **)(NULL)) &&
+ (*pass_specific != (struct collection_item *)(NULL))) {
+ TRACE_INFO_STRING("Process erros resulting from file:", file_name);
+ error = add_collection_to_collection(*error_set,NULL,NULL,*pass_specific,COL_ADD_MODE_EMBED);
+ if(error){
+ if(created) {
+ destroy_collection(*ini_config);
+ *ini_config = (struct collection_item *)(NULL);
+ }
+ destroy_collection(*error_set);
+ *error_set = (struct collection_item *)(NULL);
+ TRACE_ERROR_NUMBER("Failed to add error collection to another error collection", error);
+ return error;
+ }
+ }
+ }
+
+ TRACE_FLOW_STRING("config_to_collection", "Exit");
+ return EOK;
+}
+
+/* Reads a line from the file */
+int read_line(FILE *file,char **key,void **value, int *length, int *ext_error)
+{
+
+ char *res = NULL;
+ char buf[BUFFER_SIZE+1];
+ int len = 0;
+ char *buffer = NULL;
+ int i = 0;
+ int status = RET_INVALID;
+ char *eq = NULL;
+
+ TRACE_FLOW_STRING("read_line","Entry");
+
+ *ext_error = 0;
+
+ buffer = buf;
+
+ /* Get data from file */
+ res = fgets(buffer,BUFFER_SIZE,file);
+ if(res == NULL) {
+ TRACE_ERROR_STRING("Read nothing","");
+ return RET_EOF;
+ }
+
+ len = strlen(buffer);
+ if(len == 0) {
+ TRACE_ERROR_STRING("Nothing was read.","");
+ return RET_EMPTY;
+ }
+
+ /* Added \r just in case we deal with Windows in future */
+ if((*(buffer + len - 1) != '\n') && (*(buffer + len - 1) != '\r')) {
+ TRACE_ERROR_STRING("String it too big!","");
+ *ext_error = ERR_LONGDATA;
+ return RET_ERROR;
+ }
+
+ /* Ingnore comments */
+ if((*buffer == ';') || (*buffer == '#')) {
+ TRACE_FLOW_STRING("Comment",buf);
+ return RET_COMMENT;
+ }
+
+ TRACE_INFO_STRING("BUFFER before trimming:",buffer);
+
+ /* Trucate trailing spaces and CRs */
+ while(isspace(*(buffer + len - 1))) {
+ *(buffer + len - 1) = '\0';
+ len--;
+ }
+
+ TRACE_INFO_STRING("BUFFER after trimming trailing spaces:",buffer);
+
+ /* Trucate leading spaces */
+ while(isspace(*buffer)) {
+ buffer++;
+ len--;
+ }
+
+ TRACE_INFO_STRING("BUFFER after trimming leading spaces:",buffer);
+ TRACE_INFO_NUMBER("BUFFER length:",len);
+
+ /* Empty line */
+ if(len == 0) {
+ TRACE_FLOW_STRING("Empty line",buf);
+ return RET_EMPTY;
+ }
+
+ /* Section */
+ if(*buffer == '[') {
+ if(*(buffer+len-1) != ']') {
+ TRACE_ERROR_STRING("Invalid format for section",buf);
+ *ext_error = ERR_NOCLOSESEC;
+ return RET_ERROR;
+ }
+ buffer++;
+ len--;
+ while(isspace(*(buffer))) {
+ buffer++;
+ len--;
+ }
+ if(len == 0) {
+ TRACE_ERROR_STRING("Invalid format for section",buf);
+ *ext_error = ERR_NOSECTION;
+ return RET_ERROR;
+ }
+
+ *(buffer + len - 1) = '\0';
+ len--;
+ while(isspace(*(buffer + len - 1))) {
+ *(buffer + len - 1) = '\0';
+ len--;
+ }
+ if(len >= MAX_KEY) {
+ TRACE_ERROR_STRING("Section name is too long",buf);
+ *ext_error = ERR_SECTIONLONG;
+ return RET_ERROR;
+ }
+
+ *key = buffer;
+ return RET_SECTION;
+ }
+
+ /* Assume we are dealing with the K-V here */
+ /* Find "=" */
+ eq = strchr(buffer,'=');
+ if(eq == NULL) {
+ TRACE_ERROR_STRING("No equal sign",buf);
+ *ext_error = ERR_NOEQUAL;
+ return RET_BEST_EFFORT;
+ }
+
+ len -= eq-buffer;
+
+ /* Strip spaces around "=" */
+ i = eq - buffer - 1;
+ while((i >= 0) && isspace(*(buffer + i))) i--;
+ if(i<0) {
+ TRACE_ERROR_STRING("No key",buf);
+ *ext_error = ERR_NOKEY;
+ return RET_BEST_EFFORT;
+ }
+
+ /* Copy key into provided buffer */
+ if(i >= MAX_KEY) {
+ TRACE_ERROR_STRING("Section name is too long",buf);
+ *ext_error = ERR_LONGKEY;
+ return RET_BEST_EFFORT;
+ }
+ *key = buffer;
+ *(buffer+i+1) = '\0';
+ TRACE_INFO_STRING("KEY:",*key);
+
+ eq++;
+ len--;
+ while(isspace(*eq)) {
+ eq++;
+ len--;
+ }
+
+ *value = eq;
+ /* Make sure we include trailing 0 into data */
+ *length = len + 1;
+
+ TRACE_INFO_STRING("VALUE:",*value);
+ TRACE_INFO_NUMBER("LENGTH:",*length);
+
+ TRACE_FLOW_STRING("read_line","Exit");
+ return RET_PAIR;
+}
+
+
+/* Print errors and warnings that were detected while parsing one file */
+void print_file_parsing_errors(FILE *file,
+ struct collection_item *error_list)
+{
+ struct collection_iterator *iterator;
+ int error;
+ struct collection_item *item = (struct collection_item *)(NULL);
+ struct collection_header *header;
+ struct parse_error *pe;
+ int count;
+
+ TRACE_FLOW_STRING("print_file_parsing_errors", "Entry");
+
+ /* If we have something to print print it */
+ if(error_list == (struct collection_item *)(NULL)) {
+ TRACE_ERROR_STRING("No error list","");
+ return;
+ }
+
+ /* Make sure we go the right collection */
+ if(!is_of_class(error_list,COL_CLASS_INI_PERROR)) {
+ TRACE_ERROR_STRING("Wrong collection class:",WRONG_COLLECTION);
+ fprintf(file,"%s\n",WRONG_COLLECTION);
+ return;
+ }
+
+ /* Bind iterator */
+ error = bind_iterator(&iterator,error_list,COL_TRAVERSE_DEFAULT);
+ if(error) {
+ TRACE_ERROR_STRING("Error (bind):",FAILED_TO_PROCCESS);
+ fprintf(file,"%s\n",FAILED_TO_PROCCESS);
+ return;
+ }
+
+ do {
+ /* Loop through a collection */
+ error = iterate_collection(iterator, &item);
+ if(error) {
+ TRACE_ERROR_STRING("Error (iterate):",FAILED_TO_PROCCESS);
+ fprintf(file,"%s\n",FAILED_TO_PROCCESS);
+ unbind_iterator(iterator);
+ return;
+ }
+
+ /* Are we done ? */
+ if(item == (struct collection_item *)(NULL)) break;
+
+ /* Process collection header */
+ if(get_item_type(item) == COL_TYPE_COLLECTION) {
+ get_collection_count(item, &count);
+ if(count > 1) fprintf(file,ERROR_HEADER,get_item_property(item,NULL));
+ else break;
+ }
+ else {
+ /* Put error into provided format */
+ pe = (struct parse_error *)(get_item_data(item));
+ fprintf(file,LINE_FORMAT,
+ get_item_property(item,NULL), /* Error or warning */
+ pe->error, /* Error */
+ pe->line, /* Line */
+ parsing_error_str(pe->error)); /* Error str */
+ }
+
+ }
+ while(1);
+
+ /* Do not forget to unbind iterator - otherwise there will be a leak */
+ unbind_iterator(iterator);
+
+ TRACE_FLOW_STRING("print_file_parsing_errors", "Exit");
+}
+
+
+/* Print errors and warnings that were detected parsing configuration as a whole */
+void print_config_parsing_errors(FILE *file,struct collection_item *error_list)
+{
+ struct collection_iterator *iterator;
+ int error;
+ struct collection_item *item = (struct collection_item *)(NULL);
+ struct collection_item *file_errors = (struct collection_item *)(NULL);
+
+ TRACE_FLOW_STRING("print_config_parsing_errors", "Entry");
+
+ /* If we have something to print print it */
+ if(error_list == (struct collection_item *)(NULL)) {
+ TRACE_ERROR_STRING("No error list","");
+ return;
+ }
+
+ /* Make sure we go the right collection */
+ if(!is_of_class(error_list,COL_CLASS_INI_PESET)) {
+ TRACE_ERROR_STRING("Wrong collection class:",WRONG_COLLECTION);
+ fprintf(file,"%s\n",WRONG_COLLECTION);
+ return;
+ }
+
+ /* Bind iterator */
+ error = bind_iterator(&iterator,error_list,COL_TRAVERSE_DEFAULT);
+ if(error) {
+ TRACE_ERROR_STRING("Error (bind):",FAILED_TO_PROCCESS);
+ fprintf(file,"%s\n",FAILED_TO_PROCCESS);
+ return;
+ }
+
+ do {
+ /* Loop through a collection */
+ error = iterate_collection(iterator, &item);
+ if(error) {
+ TRACE_ERROR_STRING("Error (iterate):",FAILED_TO_PROCCESS);
+ fprintf(file,"%s\n",FAILED_TO_PROCCESS);
+ unbind_iterator(iterator);
+ return;
+ }
+
+ /* Are we done ? */
+ if(item == (struct collection_item *)(NULL)) break;
+
+ /* Print per file sets of errors */
+ if(get_item_type(item) == COL_TYPE_COLLECTIONREF) {
+ /* Extract a sub collection */
+ error = get_reference_from_item(item,&file_errors);
+ if(error) {
+ TRACE_ERROR_STRING("Error (extract):",FAILED_TO_PROCCESS);
+ fprintf(file,"%s\n",FAILED_TO_PROCCESS);
+ return;
+ }
+ print_file_parsing_errors(file,file_errors);
+ destroy_collection(file_errors);
+ }
+ }
+ while(1);
+
+ /* Do not forget to unbind iterator - otherwise there will be a leak */
+ unbind_iterator(iterator);
+
+ TRACE_FLOW_STRING("print_config_parsing_errors", "Exit");
+}
+
+
+/* Function to get value from the configration handle */
+int get_config_item(const char *section,
+ const char *name,
+ struct collection_item *ini_config,
+ struct collection_item **item)
+{
+ int error = EOK;
+ struct collection_item *section_handle = (struct collection_item *)(NULL);
+ char *to_find;
+ char default_section[] = INI_DEFAULT_SECTION;
+
+ TRACE_FLOW_STRING("get_config_item", "Entry");
+
+ /* Do we have the accepting memory ? */
+ if(item == (struct collection_item **)(NULL)) {
+ TRACE_ERROR_NUMBER("No buffer - invalid argument.", EINVAL);
+ return EINVAL;
+ }
+
+ /* Is the collection of a right type */
+ if(!is_of_class(ini_config,COL_CLASS_INI_CONFIG)) {
+ TRACE_ERROR_NUMBER("Wrong collection type", EINVAL);
+ return EINVAL;
+ }
+
+ *item == (struct collection_item *)(NULL);
+
+ if(section == NULL) to_find = default_section;
+ else to_find = (char *)section;
+
+ TRACE_INFO_STRING("Getting Name:", name);
+ TRACE_INFO_STRING("In Section:", section);
+
+ /* Get Subcollection */
+ error = get_collection_reference(ini_config,&section_handle,to_find);
+ /* Check error */
+ if((error) && (error != ENOENT)) {
+ TRACE_ERROR_NUMBER("Failed to get section", error);
+ return error;
+ }
+
+ /* Did we find a section */
+ if((error == ENOENT) || (section_handle == (struct collection_item *)(NULL))) {
+ /* We have not found section - return success */
+ TRACE_FLOW_STRING("get_value_from_config", "No such section");
+ return EOK;
+ }
+
+ /* Get item */
+ error = get_item(section_handle,(char *)name, COL_TYPE_STRING, COL_TRAVERSE_ONELEVEL, item);
+
+ TRACE_FLOW_NUMBER("get_config_item returning", error);
+ return error;
+}
+
+/* Get long value from config item */
+long get_long_config_value(struct collection_item *item, int strict, long def, int *error)
+{
+ char *endptr, *str;
+ long val = 0;
+
+ TRACE_FLOW_STRING("get_long_config_value", "Entry");
+
+ /* Do we have the item ? */
+ if((item == (struct collection_item *)(NULL)) ||
+ (get_item_type(item) != COL_TYPE_STRING)) {
+ TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+ if(error) *error = EINVAL;
+ return def;
+ }
+
+ if(error) *error = EOK;
+
+ /* Try to parse the value */
+ str = (char *)(get_item_data(item));
+ errno = 0;
+ val = strtol(str, &endptr, 10);
+
+ /* Check for various possible errors */
+ if (((errno == ERANGE) &&
+ ((val == LONG_MAX) ||
+ (val == LONG_MIN))) ||
+ ((errno != 0) && (val == 0)) ||
+ (endptr == str)) {
+ TRACE_ERROR_NUMBER("Conversion failed", EIO);
+ if(error) *error = EIO;
+ return def;
+ }
+
+ if ((strict) && (*endptr != '\0')) {
+ TRACE_ERROR_NUMBER("More characters than expected", EIO);
+ if(error) *error = EIO;
+ val = def;
+ }
+
+ TRACE_FLOW_NUMBER("get_long_config_value returning", val);
+ return val;
+}
+
+/* Get integer value from config item */
+inline int get_int_config_value(struct collection_item *item, int strict, int def, int *error)
+{
+ return (int)get_long_config_value(item, strict, (long)def, error);
+}
+
+/* Get unsigned integer value from config item */
+unsigned get_unsigned_config_value(struct collection_item *item, int strict, unsigned def, int *error)
+{
+ return (unsigned int)get_long_config_value(item, strict, (long)def, error);
+}
+
+/* Get unsigned long value from config item */
+unsigned long get_ulong_config_value(struct collection_item *item, int strict, unsigned long def, int *error)
+{
+ return (unsigned long)get_long_config_value(item, strict, (long)def, error);
+}
+
+/* Get double value */
+double get_double_config_value(struct collection_item *item, int strict, double def, int *error)
+{
+ char *endptr, *str;
+ double val = 0;
+
+ TRACE_FLOW_STRING("get_double_config_value", "Entry");
+
+ /* Do we have the item ? */
+ if((item == (struct collection_item *)(NULL)) ||
+ (get_item_type(item) != COL_TYPE_STRING)) {
+ TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+ if(error) *error = EINVAL;
+ return def;
+ }
+
+ if(error) *error = EOK;
+
+ /* Try to parse the value */
+ str = (char *)(get_item_data(item));
+ errno = 0;
+ val = strtod(str, &endptr);
+
+ /* Check for various possible errors */
+ if ((errno == ERANGE) ||
+ ((errno != 0) && (val == 0)) ||
+ (endptr == str)) {
+ TRACE_ERROR_NUMBER("Conversion failed", EIO);
+ if(error) *error = EIO;
+ return def;
+ }
+
+ if ((strict) && (*endptr != '\0')) {
+ TRACE_ERROR_NUMBER("More characters than expected", EIO);
+ if(error) *error = EIO;
+ val = def;
+ }
+
+ TRACE_FLOW_NUMBER("get_double_config_value returning", val);
+ return val;
+}
+
+/* Get boolean value */
+unsigned char get_bool_config_value(struct collection_item *item, unsigned char def, int *error)
+{
+ char *str;
+ int len;
+
+ TRACE_FLOW_STRING("get_bool_config_value", "Entry");
+
+ /* Do we have the item ? */
+ if((item == (struct collection_item *)(NULL)) ||
+ (get_item_type(item) != COL_TYPE_STRING)) {
+ TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+ if(error) *error = EINVAL;
+ return def;
+ }
+
+ if(error) *error = EOK;
+
+ str = (char *)(get_item_data(item));
+ len = get_item_length(item);
+
+ /* Try to parse the value */
+ if((strncasecmp(str,"true",len) == 0) ||
+ (strncasecmp(str,"yes",len) == 0)) {
+ TRACE_FLOW_STRING("Returning", "true");
+ return '\1';
+ }
+ else if((strncasecmp(str,"false",len) == 0) ||
+ (strncasecmp(str,"no",len) == 0)) {
+ TRACE_FLOW_STRING("Returning", "false");
+ return '\0';
+ }
+
+ TRACE_ERROR_STRING("Returning", "error");
+ if(error) *error = EIO;
+ return def;
+}
+
+/* Return a string out of the value */
+inline char *get_string_config_value(struct collection_item *item, int dup, int *error)
+{
+ char *str = NULL;
+
+ TRACE_FLOW_STRING("get_string_config_value", "Entry");
+
+ /* Do we have the item ? */
+ if((item == (struct collection_item *)(NULL)) ||
+ (get_item_type(item) != COL_TYPE_STRING)) {
+ TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+ if(error) *error = EINVAL;
+ return NULL;
+ }
+
+ /* If we are told to dup the value */
+ if(dup) {
+ errno = 0;
+ str = strdup((char *)(get_item_data(item)));
+ if(str == NULL) {
+ TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
+ if(error) *error = ENOMEM;
+ return NULL;
+ }
+ }
+ else str = (char *)(get_item_data(item));
+
+ if(error) *error = EOK;
+
+ TRACE_FLOW_STRING("get_string_config_value", "Exit");
+ return str;
+}
+
+/* A special hex format is assumed.
+ * The string should be taken in single quotes
+ * and consist of hex encoded value two hex digits per byte.
+ * Example: '0A2BFECC'
+ * Case does not matter.
+ */
+char *get_bin_config_value(struct collection_item *item, int *length, int *error)
+{
+ int i;
+ char *value = NULL;
+ char *buff;
+ int size = 0;
+ unsigned char hex;
+ int len;
+ char *str;
+
+ TRACE_FLOW_STRING("get_bin_config_value", "Entry");
+
+ /* Do we have the item ? */
+ if((item == (struct collection_item *)(NULL)) ||
+ (get_item_type(item) != COL_TYPE_STRING)) {
+ TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+ if(error) *error = EINVAL;
+ return NULL;
+ }
+
+ /* Check the length */
+ len = get_item_length(item)-1;
+ if((len/2) *2 != len) {
+ TRACE_ERROR_STRING("Invalid length for binary data","")
+ if(error) *error = EINVAL;
+ return NULL;
+ }
+
+ str = (char *)(get_item_data(item));
+
+ /* Is the format correct ? */
+ if((*str != '\'') ||
+ (*(str + len -1) != '\'')) {
+ TRACE_ERROR_STRING("String is not escaped","")
+ if(error) *error = EIO;
+ return NULL;
+ }
+
+ /* Check that all the symbols are ok */
+ buff = str + 1;
+ len -= 2;
+ for(i=0;i<len;i+=2) {
+ if((!isxdigit(*(buff+i))) || (!isxdigit(*(buff+i+1)))) {
+ TRACE_ERROR_STRING("Invalid encoding for binary data",buff+i)
+ if(error) *error = EIO;
+ return NULL;
+ }
+ }
+
+ /* The value is good so we can allocate memory for it */
+ value = malloc(len/2);
+ if(value == NULL) {
+ TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
+ if(error) *error = ENOMEM;
+ return NULL;
+ }
+
+ /* Convert the value */
+ for(i=0;i<len;i+=2) {
+ if(isdigit(*(buff+i))) {
+ if(isdigit(*(buff+i+1))) hex = 16 * (*(buff+i) - '0') + (*(buff+i+1) - '0');
+ else hex = 16 * (*(buff+i) - '0') + (tolower(*(buff+i+1)) - 'a' + 10);
+ }
+ else {
+ if(isdigit(*(buff+i+1))) hex = 16 * (tolower(*(buff+i)) - 'a') + (*(buff+i+1) - '0');
+ else hex = 16 * (tolower(*(buff+i)) - 'a' + 10) + (tolower(*(buff+i+1)) - 'a' + 10);
+ }
+
+ *(value+size) = (char)(hex);
+ size++;
+ }
+
+ if(error) *error = EOK;
+ if(length) *length = size;
+ TRACE_FLOW_STRING("get_bin_config_value", "Exit");
+ return value;
+}
+
+/* Function to free binary configuration value */
+inline void free_bin_config_value(char *value)
+{
+ if(value) free(value);
+}
+
+/* Arrays of stings and integers */
+char **get_string_config_array(struct collection_item *item, char *sep, int *size, int *error)
+{
+ char defsep[] = ",";
+ char *copy = NULL;
+ char *dest = NULL;
+ int total = 0;
+ int lensep;
+ char *buff;
+ int count = 0;
+ int len = 0;
+ int resume_len;
+ char **array;
+ char *start;
+ int i,j,k;
+ int growlen = 0;
+
+ TRACE_FLOW_STRING("get_string_config_array", "Entry");
+
+ /* Do we have the item ? */
+ if((item == (struct collection_item *)(NULL)) ||
+ (get_item_type(item) != COL_TYPE_STRING)) {
+ TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+ if(error) *error = EINVAL;
+ return NULL;
+ }
+
+ /* Handle the separators */
+ if(sep == NULL) sep = defsep;
+ lensep = strnlen(sep,3);
+
+ /* Allocate memory for the copy of the string */
+ copy = malloc(get_item_length(item));
+ if(copy == NULL) {
+ TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
+ if(error) *error = ENOMEM;
+ return NULL;
+ }
+
+ /* Loop through the string */
+ dest = copy;
+ buff = item->data;
+ start = buff;
+ for(i=0;i<item->length;i++) {
+ growlen = 1;
+ for(j=0;j<lensep;j++) {
+ if(*(buff+i) == *(sep+j)) {
+ /* If we found one of the separators trim spaces around */
+ resume_len = len;
+ while(len > 0) {
+ if(isspace(*(start+len - 1))) len--;
+ else break;
+ }
+ if(len > 0) {
+ /* Save block aside */
+ memcpy(dest,start,len);
+ count++;
+ dest+=len;
+ *dest='\0';
+ dest++;
+ len = 0;
+ /* Move forward and trim spaces if any */
+ start += resume_len + 1;
+ i++;
+ TRACE_INFO_STRING("Remaining buffer :", start);
+ TRACE_INFO_STRING("Other pointer :", buff + i);
+ k = 0;
+ while(((i+k) < item->length) && (isspace(*start))) {
+ k++;
+ start++;
+ }
+ TRACE_INFO_STRING("Remaining buffer after triming spaces:", start);
+ if(k) i+=k-1;
+ /* Next iteration of the loop will add 1 */
+ }
+ /* Break out of the inner loop */
+ growlen = 0;
+ break;
+ }
+ }
+ if(growlen) len++;
+ }
+
+ /* Copy the remaining piece */
+ memcpy(dest,start,len);
+ count++;
+ dest+=len;
+ dest='\0';
+ dest++;
+
+ /* Now we know how many items are there in the list */
+ array = malloc((count + 1) * sizeof(char *));
+ if(array == NULL) {
+ free(copy);
+ TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
+ if(error) *error = ENOMEM;
+ return NULL;
+ }
+
+ /* Loop again to fill in the pointers */
+ start = copy;
+ for(i=0;i<count;i++) {
+ *(array+i) = start;
+ while(*start!='\0') start++;
+ start++;
+ }
+ *(array+count) = NULL;
+
+ if(error) *error = EOK;
+ if(size) *size = count;
+ TRACE_FLOW_STRING("get_string_config_array", "Exit");
+ return array;
+}
+
+/* Special function to free string config array */
+void free_string_config_array(char **str_config)
+{
+ TRACE_FLOW_STRING("free_string_config_array", "Entry");
+
+ if(str_config != NULL) {
+ if(*str_config !=NULL) free(*str_config);
+ free(str_config);
+ }
+
+ TRACE_FLOW_STRING("free_string_config_array", "Exit");
+}
+
+/* Get an array of long values */
+long *get_long_config_array(struct collection_item *item, int *size, int *error)
+{
+ char *endptr, *str;
+ long val = 0;
+ long *array;
+ int count = 0;
+
+ TRACE_FLOW_STRING("get_long_config_array", "Entry");
+
+ /* Do we have the item ? */
+ if((item == (struct collection_item *)(NULL)) ||
+ (get_item_type(item) != COL_TYPE_STRING) ||
+ (size == (int *)(NULL))) {
+ TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+ if(error) *error = EINVAL;
+ return (long *)(NULL);
+ }
+
+ /* Assume that we have maximum number of different numbers */
+ array = (long *)malloc(sizeof(long) * get_item_length(item)/2);
+ if(array == NULL) {
+ TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
+ if(error) *error = ENOMEM;
+ return (long *)(NULL);
+ }
+
+ /* Now parse the string */
+ str = (char *)(get_item_data(item));
+ while(*str != '\0') {
+ errno = 0;
+ val = strtol(str, &endptr, 10);
+ if (((errno == ERANGE) &&
+ ((val == LONG_MAX) ||
+ (val == LONG_MIN))) ||
+ ((errno != 0) && (val == 0)) ||
+ (endptr == str)) {
+ TRACE_ERROR_NUMBER("Conversion failed", EIO);
+ free(array);
+ if(error) *error = EIO;
+ return (long *)(NULL);
+ }
+ /* Save value */
+ *(array + count) = val;
+ count++;
+ /* Are we done? */
+ if(*endptr == 0) break;
+ /* Advance to the next valid number */
+ str = endptr;
+ while(!isdigit(*str) && (*str != 0) && (*str != '-') && (*str != '+')) str++;
+ }
+
+ *size = count;
+ if(error) *error = EOK;
+
+ TRACE_FLOW_NUMBER("get_long_config_value returning", val);
+ return array;
+
+}
+
+/* Special function to free long config array */
+inline void free_long_config_array(long *array)
+{
+ if(array != (long *)(NULL)) free(array);
+}
+