/* INI LIBRARY Reading configuration from INI file and storing as a collection. Copyright (C) Dmitri Pal 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 . */ #define _GNU_SOURCE #include #include #include #include #include #include "config.h" /* For error text */ #include #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 int read_line(FILE *file,char **key,char **value, int *length, int *ext_error); /* 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 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,¤t_section,INI_DEFAULT_SECTION); if(error != EOK) { /* Create default collection */ if((error=create_collection(¤t_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,¤t_section,key); if(error != EOK) { /* Create default collection */ if((error=create_collection(¤t_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); 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,char **value, int *length, int *ext_error) { char *res = NULL; char buf[BUFFER_SIZE+1]; int len = 0; char *buffer = NULL; int i = 0; 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 parse_error *pe; unsigned 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,§ion_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;idata; start = buff; for(i=0;ilength;i++) { growlen = 1; for(j=0;j 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