/* ELAPI Reading configuration from INI file and storing as collection. Copyright (C) Dmitri Pal 2009 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include "elapi_collection.h" #include "elapi_ini.h" #include "elapi_tools.h" #include "elapi_debug.h" /* STATIC INTERNAL FUNCTIONS */ #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 #define NAME_OVERHEAD 10 #define MAX_KEY 65535 #define MAX_VALUE 65535 #define BUFFER_SIZE MAX_KEY + MAX_VALUE + 3 #define SLASH "/" /* Special handler to extract configuration value */ static int value_extractor(char *property, int property_len, int type, void *data, int length, void *custom_data, int *stop) { void *allocated = NULL; DEBUG_STRING("value_extractor","Entry."); DEBUG_STRING("Property:",property); DEBUG_NUMBER("Property length:",property_len); DEBUG_NUMBER("Type:",type); DEBUG_NUMBER("Length:",length); allocated = malloc(length); if(allocated == NULL) return errno; memcpy(allocated, data, length); *((int **)(custom_data)) = allocated; *stop = 1; DEBUG_STRING("value_extractor","Success Exit."); return EOK; } /* Process value */ static int process_value(char *buff, void *value, int *length, int *type) { int len = 0; int i = 0; int esc = 0; unsigned char hex = 0; int mod = 0; char *dp = NULL; char *dp2 = NULL; int use_long = 0; int use_unsigned = 0; long l_result = 0; unsigned long ul_result = 0; unsigned u_result = 0; int result = 0; double dbl = 0.; char *errconv = NULL; DEBUG_STRING("process_value","Entry") len = strlen(buff); if(len == 0) { DEBUG_STRING("Empty value","") return RET_INVALID; } if(len >= MAX_VALUE) { DEBUG_STRING("Value too big","") return RET_INVALID; } /* Is this a string ? */ if((*buff == '"') && (*(buff+len-1) == '"')) { buff++; len-=2; *length = 0; *type = ELAPI_TYPE_STRING; esc = 0; for(i=0;i 20) { DEBUG_STRING("Number is too long",""); return RET_INVALID; } errno = 0; dbl = (double)(strtod(buff,&errconv)); if(errno != 0) { DEBUG_STRING("Invalid double",""); return RET_INVALID; } DEBUG_STRING("Value to convert:",buff); DEBUG_DOUBLE("Got double:",dbl) *type = ELAPI_TYPE_DOUBLE; *length = sizeof(double); memcpy(value, (void *)(&dbl),*length); } DEBUG_STRING("process_value","Success"); return RET_PAIR; } /* Reads a line from the file */ static int read_line(FILE *file,char *key,void *value, int *length, int *type) { char *res = NULL; char buf[BUFFER_SIZE+1]; int len = 0; char *buffer = NULL; int i = 0; int status = RET_INVALID; char *eq = NULL; DEBUG_STRING("read_line","Entry") buffer = buf; /* Get data from file */ res = fgets(buffer,BUFFER_SIZE,file); if(res == NULL) { DEBUG_STRING("Read nothing","") return RET_EOF; } len = strlen(buffer); if(len == 0) { DEBUG_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')) { DEBUG_STRING("String it too big!","") return RET_INVALID; } /* Ingnore comments */ if(*buffer == ';') { DEBUG_STRING("Comment",buf) return RET_INVALID; } /* Empty line */ if(*buffer == '\n') { DEBUG_STRING("Empty line",buf) return RET_INVALID; } /* Trucate trailing spaces and CRs */ while(isspace(*(buffer + len - 1))) { *(buffer + len - 1) = '\0'; len--; } /* Trucate leading spaces */ while(isspace(*buffer)) { buffer++; len--; } /* Section */ if(*buffer == '[') { if(*(buffer+len-1) != ']') { DEBUG_STRING("Invalid format for section",buf) return RET_ERROR; } buffer++; len--; while(isspace(*(buffer))) { buffer++; len--; } if(len == 0) { DEBUG_STRING("Invalid format for section",buf) return RET_ERROR; } *(buffer + len - 1) = '\0'; len--; while(isspace(*(buffer + len - 1))) { *(buffer + len - 1) = '\0'; len--; } if(len >= MAX_KEY) { DEBUG_STRING("Section name is too long",buf) return RET_ERROR; } memcpy(key,buffer,len+1); return RET_SECTION; } /* Assume we are dealing with the K-V here */ /* Find "=" */ eq = strchr(buffer,'='); if(eq == NULL) { DEBUG_STRING("No equal sign",buf) return RET_INVALID; } /* Strip spaces around "=" */ i = eq - buffer - 1; while((i >= 0) && isspace(*(buffer + i))) i--; if(i<0) { DEBUG_STRING("No key",buf) return RET_INVALID; } /* Copy key into provided buffer */ if(i >= MAX_KEY) { DEBUG_STRING("Section name is too long",buf) return RET_INVALID; } memcpy(key,buffer,i+1); *(key+i+1) = '\0'; DEBUG_STRING("KEY:",key); eq++; while(isspace(*eq)) eq++; DEBUG_STRING("VALUE:",eq); /* Now process value */ status = process_value(eq,value,length,type); DEBUG_NUMBER("Type:",*type); DEBUG_NUMBER("Data length:",*length); return status; } /* Add to collection or update */ static int add_or_update(struct collection_item *current_section, char *key, void *value, int length, int type) { int found = ELAPI_NOMATCH; int error = EOK; DEBUG_STRING("add_or_update", "Entry"); (void)is_item_in_collection(current_section,key,ELAPI_TYPE_ANY,ELAPI_TRAVERSE_IGNORE,&found); if(found == ELAPI_MATCH) { DEBUG_STRING("Updating...", ""); error = update_property(current_section,key,type,value,length,ELAPI_TRAVERSE_IGNORE); } else { DEBUG_STRING("Adding...", ""); error = add_any_property(current_section,NULL,key,type,value,length); } DEBUG_NUMBER("add_or_update returning", error); return error; } /***************************************************************************/ /* Function to read single ini file and pupulate * the provided collection with subcollcetions from the file */ int ini_to_collection(char *filename, struct collection_item *ini_config) { FILE *file; int error; int status; int section_count = 0; char key[MAX_KEY]; char value[MAX_VALUE]; struct collection_item *current_section = (struct collection_item *)(NULL); int length; int type; DEBUG_STRING("ini_to_collection", "Entry"); /* Open file for reading */ file = fopen(filename,"r"); if(file == NULL) { error = errno; DEBUG_NUMBER("Failed to open file - but this is OK", error); return EOK; } /* Read file lines */ while((status = read_line(file,key,(void *)(value),&length,&type)) != RET_EOF) { 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)) || (error=add_collection_to_collection(ini_config,NULL,NULL, current_section, ELAPI_ADD_MODE_REFERENCE))) { DEBUG_NUMBER("Failed to create collection", error); fclose(file); destroy_collection(current_section); return error; } } section_count++; } /* Put value into the collection */ if((error=add_or_update(current_section,key,value,length,type))) { DEBUG_NUMBER("Failed to add pair to collection", error); fclose(file); destroy_collection(current_section); 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)) || (error=add_collection_to_collection(ini_config,NULL,NULL, current_section, ELAPI_ADD_MODE_REFERENCE))) { DEBUG_NUMBER("Failed to add collection", error); fclose(file); destroy_collection(current_section); return error; } } section_count++; break; case RET_EMPTY: DEBUG_STRING("Empty string", ""); break; case RET_COMMENT: DEBUG_STRING("Comment", ""); break; case RET_ERROR: DEBUG_STRING("Invalid section format", ""); return EINVAL; case RET_INVALID: default: DEBUG_STRING("Invalid string", ""); break; } } /* Close file */ fclose(file); DEBUG_COLLECTION(ini_config); destroy_collection(current_section); DEBUG_COLLECTION(ini_config); DEBUG_STRING("ini_to_collection", "Success Exit"); return EOK; } /*********************************************************************/ /* read ini file and create collection out of it */ int config_to_collection(char *application, char *config_file, char *config_dir, struct collection_item **ini_config) { int error=EOK; char *file_name; DEBUG_STRING("config_to_collection", "Entry"); *ini_config = (struct collection_item *)(NULL); /* Create collection */ if((error=create_collection(ini_config,application))) { DEBUG_NUMBER("Failed to create collection", error); destroy_collection(*ini_config); return error; } /* Read master file */ if((error = ini_to_collection(config_file,*ini_config))) { DEBUG_NUMBER("Failed to read master file", error); destroy_collection(*ini_config); return error; } /* Get specific application file */ file_name = malloc(strlen(config_dir) + strlen(application) + NAME_OVERHEAD); if(file_name == NULL) { error = errno; DEBUG_NUMBER("Failed to allocate memory for file name", error); destroy_collection(*ini_config); return error; } sprintf(file_name,"%s%s%s.conf",config_dir, SLASH, application); DEBUG_STRING("Opening file:", file_name); /* Read master file */ error = ini_to_collection(file_name,*ini_config); free(file_name); if(error) { DEBUG_NUMBER("Failed to read specific application file", error); destroy_collection(*ini_config); return error; } DEBUG_STRING("config_to_collection", "Exit"); return EOK; } /* Function to get value from the configration handle */ int get_value_from_config(void *value, int type, char *section, char *name, struct collection_item *ini_config) { int error = EOK; struct collection_item *section_handle = (struct collection_item *)(NULL); char *to_find; char default_section[] = INI_DEFAULT_SECTION; DEBUG_STRING("get_value_from_config", "Entry"); if(section == NULL) to_find = default_section; else to_find = section; DEBUG_STRING("Getting Name:", name); DEBUG_STRING("In Section:", section); /* Get Subcollection */ error = get_collection_reference(ini_config,§ion_handle,to_find); if(section_handle == (struct collection_item *)(NULL)) { /* We have not found section - return success */ DEBUG_STRING("get_value_from_config", "No such section"); return EOK; } /* If we have the section then get the data */ error = get_item_and_do(section_handle, name,type, ELAPI_TRAVERSE_ONELEVEL, value_extractor, value); DEBUG_STRING("get_value_from_config", "Exit"); return error; }