diff options
author | Dmitri Pal <dpal@redhat.com> | 2009-04-06 09:42:51 -0400 |
---|---|---|
committer | Stephen Gallagher <sgallagh@redhat.com> | 2009-04-06 15:59:21 -0400 |
commit | 12454b937b53358e1dbb29dc7649e3f88b9bfd2d (patch) | |
tree | 8bb1a5b726772b230c7802ccdf3475d47c298f64 /common/ini | |
parent | de6d6fb21bf367a94c7d292f2c3decd24cf42868 (diff) | |
download | sssd-12454b937b53358e1dbb29dc7649e3f88b9bfd2d.tar.gz sssd-12454b937b53358e1dbb29dc7649e3f88b9bfd2d.tar.bz2 sssd-12454b937b53358e1dbb29dc7649e3f88b9bfd2d.zip |
First attempt to produce INI interface.
Diffstat (limited to 'common/ini')
-rw-r--r-- | common/ini/Makefile.am | 17 | ||||
-rw-r--r-- | common/ini/ini.conf | 55 | ||||
-rw-r--r-- | common/ini/ini.d/real.conf | 52 | ||||
-rw-r--r-- | common/ini/ini.d/test.conf | 41 | ||||
-rw-r--r-- | common/ini/ini_config.c | 1286 | ||||
-rw-r--r-- | common/ini/ini_config.h | 160 | ||||
-rw-r--r-- | common/ini/ini_config_ut.c | 701 |
7 files changed, 2312 insertions, 0 deletions
diff --git a/common/ini/Makefile.am b/common/ini/Makefile.am new file mode 100644 index 00000000..a0131e94 --- /dev/null +++ b/common/ini/Makefile.am @@ -0,0 +1,17 @@ +#DEBUG_FLAGS=@DEBUG_VAR@ +TRACE_LEVEL=@TRACE_VAR@ + +topdir=.. +AM_CPPFLAGS = -I$(topdir) -I$(topdir)/trace -I$(topdir)/collection $(TRACE_LEVEL) + +# Build static libraty +noinst_LIBRARIES = libini_config.a +libini_config_a_SOURCES = ini_config.c collection.h trace.h collection_tools.h collection_class.h ini_config.h + +# Build shared library +#lib_LTLIBRARIES = libini.la +#libini_la_SOURCES = ini_config.c collection.h trace.h collection_tools.h collection_class.h ini_config.h + +noinst_PROGRAMS = ini_config_ut +ini_config_ut_SOURCES = ini_config_ut.c +ini_config_ut_LDADD = libini_config.a -lm -lz ../collection/libcollection.a diff --git a/common/ini/ini.conf b/common/ini/ini.conf new file mode 100644 index 00000000..0d0c7edb --- /dev/null +++ b/common/ini/ini.conf @@ -0,0 +1,55 @@ +# INI file that is used by unit test + +data = ds,mf.ds,mf.ds,m.ds,mds.,fmds. + dskjh = mdsmdssm ,md.sd, + C1 = "abcd" +;test data + +wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww = haha + + [ + + ] + + [ wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww ] + + +;more data + + = "nmjdsbfmsdn" + s = "nmjdsbfmsdn + + HEX1 = 'ABV1' + HEX2 = 'ABV' + [ ttt ] + +C2 ='Ab' + +int=-44i44 +int2=-66U + +c = + +with creame and sugar + + [ dddd ] + +DC1 = 123U + +f=-9.0.0 +DC2 = -235L +DC3 = -345.8907 + + + [ zoo + + something + + + [ ttt ] + +v=55555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555 +v1=55555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555.55555555555555555555555555555555 + +C3 = -34756.56 +C4 = .9 diff --git a/common/ini/ini.d/real.conf b/common/ini/ini.d/real.conf new file mode 100644 index 00000000..f026bd89 --- /dev/null +++ b/common/ini/ini.d/real.conf @@ -0,0 +1,52 @@ +[config] +version = 0.1 + +[monitor] +description = Monitor Configuration +sbusTimeout = 10 +sbusAddress = unix:path=/var/lib/sss/pipes/private/dbus +servicePingTime = 10 +bad_number = 5a + +[services] +description = Local service configuration +activeServices = dp, nss, pam, info + +[services/dp] +description = Data Provider Configuration +command = /usr/libexec/sssd/sssd_dp + +[services/nss] +description = NSS Responder Configuration +unixSocket = /var/lib/sss/pipes/nss +command = /usr/libexec/sssd/sssd_nss + +[services/pam] +command = /usr/libexec/sssd/sssd_pam +description = PAM Responder Configuration +unixSocket = /var/lib/sss/pipes/pam + +[services/info] +description = InfoPipe Configuration +command = ./sbin/sssd_info + +[domains] +domainsOrder = LOCAL, EXAMPLE.COM , SOMEOTHER.COM + +[domains/LOCAL] +description = Reserved domain for local configurations +legacy = FALSE +enumerate = 3 + +[domains/EXAMPLE.COM] +description = Example domain served by IPA +provider = ipa +server = ipaserver1.example.com +server = ipabackupserver.example.com +legacy = FALSE +enumerate = 0 +binary_test = '010203' +long_array = 1, 2; 4' ;8p .16/ 32? + + + diff --git a/common/ini/ini.d/test.conf b/common/ini/ini.d/test.conf new file mode 100644 index 00000000..68e9c674 --- /dev/null +++ b/common/ini/ini.d/test.conf @@ -0,0 +1,41 @@ + +data = ds,mf.ds,mf.ds,m.ds,mds.,fmds. + dskjh = mdsmdssm ,md.sd, + C1 = "abcd" +;test data + + + +;more data + + = "nmjdsbfmsdn" + s = "nmjdsbfmsdn + + HEX1 = 'ABV1' + HEX2 = 'ABV' + [ ttt ] + +C2 ='AbBB' + +int=-44i44 +int2=-66U + +c = + + + [ dddd1 ] + +DC1 = 123U + +f=-9.0.0 +DC2 = -235L +DC3 = -345.8907 + + + [ ttt2 ] + +v=55555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555 +v1=55555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555.55555555555555555555555555555555 + +C3 = -34756.56 +C4 = .9 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,¤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); + 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,§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;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); +} + diff --git a/common/ini/ini_config.h b/common/ini/ini_config.h new file mode 100644 index 00000000..8bf7373b --- /dev/null +++ b/common/ini/ini_config.h @@ -0,0 +1,160 @@ +/* + INI LIBRARY + + Header file for 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/>. +*/ + +#ifndef INI_CONFIG_H +#define INI_CONFIG_H + +#include <limits.h> +#include "collection.h" + +/* Name of the default (missing section in the INI file */ +#define INI_DEFAULT_SECTION "default" + +/* Collection classes used in INI processing */ +#define COL_CLASS_INI_BASE 20000 +#define COL_CLASS_INI_CONFIG COL_CLASS_INI_BASE + 0 /* Class for configuration collection. Implies a collection of sections */ +#define COL_CLASS_INI_SECTION COL_CLASS_INI_BASE + 1 /* A one level collection of key value pairs where values are always stings */ +#define COL_CLASS_INI_PERROR COL_CLASS_INI_BASE + 2 /* A one level collection of parse errors - store parse_error structs */ +#define COL_CLASS_INI_PESET COL_CLASS_INI_BASE + 3 /* A one level collection of parse error collections */ + + +/* Error levels */ +#define INI_STOP_ON_ANY 0 /* Fail if any problem is detected */ +#define INI_STOP_ON_NONE 1 /* Best effort - do not fail */ +#define INI_STOP_ON_ERROR 2 /* Fail on errors only */ + + +/* Parsing errors and warnings */ +#define ERR_LONGDATA 1 /* Error */ +#define ERR_NOCLOSESEC 2 /* Error */ +#define ERR_NOSECTION 3 /* Error */ +#define ERR_SECTIONLONG 4 /* Error */ +#define ERR_NOEQUAL 5 /* Warning */ +#define ERR_NOKEY 6 /* Warning */ +#define ERR_LONGKEY 7 /* Warning */ + +#define ERR_MAXPARSE ERR_LONGKEY + +#ifdef HAVE_INI_BEST_EFFORT /* Ignore bad lines in the INI files */ +#define RET_BEST_EFFORT RET_INVALID +#else +#define RET_BEST_EFFORT RET_ERROR +#endif + +/* Internal sizes */ +/* FIXME - make them configurable via config.h */ +#define MAX_KEY 1024 +#define MAX_VALUE PATH_MAX +#define BUFFER_SIZE MAX_KEY + MAX_VALUE + 3 + +struct parse_error { + unsigned line; + int error; +}; + +/* Function to return parsing error */ +const char *parsing_error_str(int parsing_error); + +/* Read configuration information from a file */ +int config_from_file(const char *application, /* Name of the application - will be used as name of the collection */ + const char *config_file, /* Name of the config file - if NULL the collection will be empty */ + struct collection_item **ini_config, /* If *ini_config is NULL a new ini object will be allocated, */ + /* otherwise the one that is pointed to will be updated. */ + int error_level, /* Error level - break for erros, warnings or best effort (don't break) */ + struct collection_item **error_list); /* List of errors for a file */ + + +/* Read default config file and then overwrite it with a specific one from the directory */ +int config_for_app(const char *application, /* Name of the application that will be used to get config for */ + const char *config_file, /* Name of the configuration file with default settings for all apps */ + const char *config_dir, /* Name of the directory where the configuration files for different apps will be dropped */ + struct collection_item **ini_config, /* New config object */ + int error_level, /* Level of error tolerance */ + struct collection_item **error_set); /* Collection of collections of parsing errors */ + +/* Print errors and warnings that were detected while parsing one file */ +/* Use this function to print results of the config_from_file() call */ +void print_file_parsing_errors(FILE *file, /* File to send errors to */ + struct collection_item *error_list); /* List of parsing errors */ + +/* Print errors and warnings that were detected parsing configuration as a whole */ +/* Use this function to print results of the config_for_app() call */ +void print_config_parsing_errors(FILE *file, /* File to send errors to */ + struct collection_item *error_list); /* Collection of collections of errors */ + +/* Get a configuration item form the configuration */ +int get_config_item(const char *section, /* Section. If NULL assumed default */ + const char *name, /* Name of the property to look up */ + struct collection_item *ini_config, /* Collection to search */ + struct collection_item **item); /* Item returned. Will be NULL is not found. / + +/* Convertion functions for the configuration item. + * Sets error to EINVAL if the item is bad. + * Sets error to EIO if the conversion failed. + * These functions do not allocate memory. + * They always return best effort conversion value. + * In case of error they return provided default. + * It is up to the caller to check an error and take an action. + */ +/* If "strict" parameter is non zero the function will fail if there are more + * characters after last digit. + */ +int get_int_config_value(struct collection_item *item, int strict, int def, int *error); +long get_long_config_value(struct collection_item *item, int strict, long def, int *error); +unsigned get_unsigned_config_value(struct collection_item *item, int strict, unsigned def, int *error); +unsigned long get_ulong_config_value(struct collection_item *item, int strict, unsigned long def, int *error); +double get_double_config_value(struct collection_item *item, int strict, double def, int *error); +unsigned char get_bool_config_value(struct collection_item *item, unsigned char def, int *error); + +/* Function get_string_config_value returns pointer to the string out of item. + * If 'dup' is not 0 it makes a copyof the string otherwise it does not. + */ +char *get_string_config_value(struct collection_item *item, int dup, int *error); + +/* A get_bin_value and get_xxx_array functions allocate memory. + * It is the responsibility of the caller to free it after use. + * free_xxx conviniece wrappers are provided for this purpose. + * Functions will return NULL if conversion failed. + */ +/* 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); +void free_bin_config_value(char *); + +/* Array of stings */ +/* Separator sting includes up to three different separators. If NULL comma is assumed. */ +/* The spaces are trimmed automatically around separators in the string. */ +char **get_string_config_array(struct collection_item *item, char *sep, int *size, int *error); +/* Array of long values - separators are detected automatically. */ +/* The length of the allocated array is returned in "size" */ +long *get_long_config_array(struct collection_item *item, int *size, int *error); + +/* Special function to free string config array */ +void free_string_config_array(char **str_config); +/* Special function to free long config array */ +void free_long_config_array(long *array); + +#endif diff --git a/common/ini/ini_config_ut.c b/common/ini/ini_config_ut.c new file mode 100644 index 00000000..2bb5517d --- /dev/null +++ b/common/ini/ini_config_ut.c @@ -0,0 +1,701 @@ +/* + INI LIBRARY + + Unit test for the INI library. + + 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> +#define TRACE_HOME +#include "ini_config.h" +#include "collection.h" +#include "collection_tools.h" + + +int basic_test() +{ + int error; + struct collection_item *ini_config = (struct collection_item *)(NULL); + struct collection_item *error_set = (struct collection_item *)(NULL); + + error = config_for_app("test", "./ini/ini.conf", "./ini/ini.d", &ini_config,INI_STOP_ON_NONE,&error_set); + if(error) { + printf("Attempt to read configuration returned error: %d\n",error); + return error; + } + + debug_collection(ini_config,COL_TRAVERSE_DEFAULT); + print_collection(ini_config); + print_collection(error_set); + + printf("\n\n----------------------\n"); + /* Output parsing errors (if any) */ + print_config_parsing_errors(stdout,error_set); + printf("----------------------\n\n\n"); + + + destroy_collection(ini_config); + destroy_collection(error_set); + return 0; +} + +int single_file() +{ + int error; + struct collection_item *ini_config = (struct collection_item *)(NULL); + struct collection_item *error_set = (struct collection_item *)(NULL); + + error = config_from_file("test", "./ini/ini.conf", &ini_config,INI_STOP_ON_NONE,&error_set); + if(error) { + printf("Attempt to read configuration returned error: %d\n",error); + return error; + } + + debug_collection(ini_config,COL_TRAVERSE_DEFAULT); + print_collection(ini_config); + print_collection(error_set); + + printf("\n\n----------------------\n"); + /* Output parsing errors (if any) */ + print_file_parsing_errors(stdout,error_set); + printf("----------------------\n\n\n"); + + + destroy_collection(ini_config); + destroy_collection(error_set); + return 0; +} + +int negative_test() +{ + int error; + int count; + struct collection_item *ini_config = (struct collection_item *)(NULL); + struct collection_item *error_set = (struct collection_item *)(NULL); + + /* App name is null - expect failure */ + error = config_for_app(NULL, NULL, NULL, NULL,INI_STOP_ON_NONE,NULL); + if(!error) { + printf("Expected error: %d got success\n",EINVAL); + return -1; + } + + /* Config collection storage is NULL - expect failure */ + error = config_for_app("real", NULL, NULL, NULL,INI_STOP_ON_NONE,NULL); + if(!error) { + printf("Expected error: %d got success\n",EINVAL); + return -1; + } + + /* Config collection storage is NULL - expect failure */ + error = config_for_app("real", "real.conf", NULL, NULL,INI_STOP_ON_NONE,NULL); + if(!error) { + printf("Expected error: %d got success\n",EINVAL); + return -1; + } + + /* Expect success but empty config */ + error = config_for_app("real", "real.conf", NULL, &ini_config,INI_STOP_ON_NONE,NULL); + if(error) { + printf("Expected success got error: %d\n",error); + return error; + } + + count = 0; + (void)get_collection_count(ini_config,&count); + if(count > 1) { + printf("Expected empty collection but got contents with %d elements\n",count); + print_collection(ini_config); + return -1; + } + + destroy_collection(ini_config); + return 0; + +} + +int real_test(const char *file) +{ + int error; + struct collection_item *ini_config = (struct collection_item *)(NULL); + struct collection_item *error_set = (struct collection_item *)(NULL); + struct collection_iterator *iterator = (struct collection_iterator *)(NULL); + struct collection_item *item = (struct collection_item *)(NULL); + int type; + + printf("\n\n===== REAL TEST START ======\n"); + printf("Reading collection\n"); + error = config_for_app("real", file, "./ini/ini.d", &ini_config,INI_STOP_ON_NONE,&error_set); + if(error) { + printf("Attempt to read configuration returned error: %d\n",error); + return error; + } + + printf("Debugging the config collection:\n"); + debug_collection(ini_config,COL_TRAVERSE_DEFAULT); + printf("Debugging the error collection:\n"); + debug_collection(error_set,COL_TRAVERSE_DEFAULT); + + printf("About to print parsing errors:\n"); + printf("\n\n----------------------\n"); + /* Output parsing errors (if any) */ + print_config_parsing_errors(stdout,error_set); + printf("----------------------\n\n\n"); + + printf("About to bind iterator to print the config file contents.\n"); + /* Bind iterator */ + error = bind_iterator(&iterator,ini_config,COL_TRAVERSE_DEFAULT|COL_TRAVERSE_END); + if(error) { + printf("Failed to bind iterator: %d\n",error); + destroy_collection(ini_config); + destroy_collection(error_set); + return error; + } + + printf("About to start iteration loop.\n"); + do { + /* Loop through a collection */ + error = iterate_collection(iterator, &item); + if(error) { + printf("Error iterating collection: %d",error); + unbind_iterator(iterator); + return error; + } + + /* Are we done ? */ + if(item == (struct collection_item *)(NULL)) break; + + type = get_item_type(item); + + /* Start of the collection */ + if(type == COL_TYPE_COLLECTION) + printf("Contents of the configuration for application %s\n",get_item_property(item,NULL)); + /* End of section */ + else if(type == COL_TYPE_END) printf("\n"); + /* Section header ? */ + else if(type == COL_TYPE_COLLECTIONREF) printf("[%s]\n",get_item_property(item,NULL)); + /* Anything else - we know they are all strings*/ + else printf("%s = %s\n",get_item_property(item,NULL), (char *)get_item_data(item)); + } + while(1); + + /* Do not forget to unbind iterator - otherwise there will be a leak */ + printf("About to clean up.\n"); + unbind_iterator(iterator); + + destroy_collection(ini_config); + destroy_collection(error_set); + return 0; +} + +int get_test() +{ + + int error; + struct collection_item *ini_config = (struct collection_item *)(NULL); + struct collection_item *error_set = (struct collection_item *)(NULL); + struct collection_iterator *iterator = (struct collection_iterator *)(NULL); + struct collection_item *item = (struct collection_item *)(NULL); + int type; + int number; + long number_long; + double number_double; + unsigned number_unsigned; + unsigned long number_ulong; + unsigned char logical; + char *str; + void *binary; + int length; + int i; + char **strarray; + char **strptr; + int size; + long *array; + + printf("\n\n===== GET TEST START ======\n"); + printf("Reading collection\n"); + error = config_for_app("real", NULL, "./ini/ini.d", &ini_config,INI_STOP_ON_NONE,&error_set); + if(error) { + printf("Attempt to read configuration returned error: %d\n",error); + return error; + } + + printf("Debugging the config collection:\n"); + debug_collection(ini_config,COL_TRAVERSE_DEFAULT); + printf("Debugging the error collection:\n"); + debug_collection(error_set,COL_TRAVERSE_DEFAULT); + destroy_collection(error_set); + + printf("Negtive test - trying to get non existing key-value pair.\n"); + + /* Negative test */ + item = (struct collection_item *)(NULL); + error = get_config_item("monitor1","description1", ini_config, &item); + if(error) { + printf("Expected success but got error! %d\n",error); + destroy_collection(ini_config); + return error; + } + + /* Item should not be found */ + if(item != (struct collection_item *)(NULL)) { + printf("Expected NULL but got something else!\n"); + destroy_collection(ini_config); + return -1; + } + + /* Another negative test but section exists this time */ + item = (struct collection_item *)(NULL); + error = get_config_item("monitor","description1", ini_config, &item); + if(error) { + printf("Expected success but got error! %d\n",error); + destroy_collection(ini_config); + return error; + } + + /* Item should not be found */ + if(item != (struct collection_item *)(NULL)) { + printf("Expected NULL but got something else!\n"); + destroy_collection(ini_config); + return -1; + } + + printf("Trying to get an item.\n"); + + /* Positive test */ + item = (struct collection_item *)(NULL); + error = get_config_item("monitor","description", ini_config, &item); + if(error) { + printf("Expected success but got error! %d\n",error); + destroy_collection(ini_config); + return error; + } + + /* Item should be found */ + if(item == (struct collection_item *)(NULL)) { + printf("Expected item but got something NULL!\n"); + destroy_collection(ini_config); + return -1; + } + + debug_item(item); + + + printf("Get item as string without duplication from NULL item.\n"); + + /* Get a string without duplicication */ + /* Negative test */ + error = 0; + str = get_string_config_value(NULL, 0, &error); + if(!error) { + printf("Expected error got success.\n"); + destroy_collection(ini_config); + return -1; + } + + printf("Get item as string without duplication from correct item.\n"); + + /* Now get string from the right item */ + error = 0; + str = get_string_config_value(item, 0, &error); + if(error) { + printf("Expected success got error %d.\n",error); + destroy_collection(ini_config); + return error; + } + + printf("Value: [%s]\n",str); + + /* Same thing but create a dup */ + + printf("Get item as string with duplication from correct item.\n"); + + error = 0; + str = get_string_config_value(item, 1, &error); + if(error) { + printf("Expected success got error %d.\n",error); + destroy_collection(ini_config); + return error; + } + + printf("Value: [%s]\n",str); + free(str); + + + /* Get a badly formated number */ + printf("Convert item to number with strict conversion.\n"); + + item = (struct collection_item *)(NULL); + error = get_config_item("monitor","bad_number", ini_config, &item); + if(error) { + printf("Expected success but got error! %d\n",error); + destroy_collection(ini_config); + return error; + } + + /* Item should be found */ + if(item == (struct collection_item *)(NULL)) { + printf("Expected item but got something NULL!\n"); + destroy_collection(ini_config); + return -1; + } + + debug_item(item); + + + /* Now try to get value in different ways */ + error = 0; + number = get_int_config_value(item, 1, 10, &error); + if(error) { + /* We expected error in this case */ + printf("Expected error.\n"); + if(number != 10) { + printf("It failed to set default value.\n"); + destroy_collection(ini_config); + return -1; + } + } + + printf("Convert item to number without strict conversion.\n"); + + error = 0; + number = 1; + number = get_int_config_value(item, 0, 10, &error); + if(error) { + /* We expected error in this case */ + printf("Did not expect error.\n"); + destroy_collection(ini_config); + return error; + } + + if(number != 5) { + /* We expected error in this case */ + printf("We expected that the conversion will return 5.\n"); + destroy_collection(ini_config); + return -1; + } + + + + /* Get real integer */ + + printf("Fetch another item from section \"domains/LOCAL\" named \"enumerate\".\n"); + + item = (struct collection_item *)(NULL); + error = get_config_item("domains/LOCAL","enumerate", ini_config, &item); + if(error) { + printf("Expected success but got error! %d\n",error); + destroy_collection(ini_config); + return error; + } + + /* Item should be found */ + if(item == (struct collection_item *)(NULL)) { + printf("Expected success but got NULL.\n"); + destroy_collection(ini_config); + return -1; + } + + printf("Convert item to integer.\n"); + + /* Take number out of it */ + error = 0; + number = get_int_config_value(item, 1, 100, &error); + if(error) { + printf("Did not expect error. Got %d\n",error); + destroy_collection(ini_config); + return error; + } + + /* It is 3 in the file */ + if(number != 3) { + printf("We expected that the conversion will return 3.\n"); + destroy_collection(ini_config); + return -1; + } + + printf("Expected 3 got %d\n", number); + + printf("Convert item to long.\n"); + + /* Take number out of it */ + error = 0; + number_long = get_long_config_value(item, 1, 100, &error); + if(error) { + printf("Did not expect error. Got %d\n",error); + destroy_collection(ini_config); + return error; + } + + /* It is 3 in the file */ + if(number_long != 3) { + printf("We expected that the conversion will return 3.\n"); + destroy_collection(ini_config); + return -1; + } + + printf("Expected 3 got %d\n", number_long); + + printf("Convert item to unsigned.\n"); + + /* Take number out of it */ + error = 0; + number_unsigned = get_unsigned_config_value(item, 1, 100, &error); + if(error) { + printf("Did not expect error. Got %d\n",error); + destroy_collection(ini_config); + return error; + } + + /* It is 3 in the file */ + if(number_unsigned != 3) { + printf("We expected that the conversion will return 3.\n"); + destroy_collection(ini_config); + return -1; + } + + printf("Expected 3 got %d\n", number_unsigned); + + printf("Convert item to unsigned long.\n"); + + /* Take number out of it */ + error = 0; + number_ulong = get_ulong_config_value(item, 1, 100, &error); + if(error) { + printf("Did not expect error. Got %d\n",error); + destroy_collection(ini_config); + return error; + } + + /* It is 3 in the file */ + if(number_ulong != 3) { + printf("We expected that the conversion will return 3.\n"); + destroy_collection(ini_config); + return -1; + } + + printf("Expected 3 got %d\n", number_ulong); + + printf("Convert item to double.\n"); + + /* Take number out of it */ + error = 0; + number_double = get_double_config_value(item, 1, 100., &error); + if(error) { + printf("Did not expect error. Got %d\n",error); + destroy_collection(ini_config); + return error; + } + + /* It is 3 in the file */ + if(number_double != 3.) { + printf("We expected that the conversion will return 3.\n"); + destroy_collection(ini_config); + return -1; + } + + printf("Expected 3 got %d\n", number_double); + + printf("Convert item to bool.\n"); + + /* Take number out of it */ + error = 0; + logical = get_bool_config_value(item, 1, &error); + if(!error) { + printf("Expect error. Got success.\n"); + destroy_collection(ini_config); + return -1; + } + + /* Get real bool item and convert it */ + printf("Get real bool item \"legacy\" and convert it.\n"); + + item = (struct collection_item *)(NULL); + error = get_config_item("domains/LOCAL","legacy", ini_config, &item); + if(error) { + printf("Expected success but got error! %d\n",error); + destroy_collection(ini_config); + return error; + } + + /* Item should be found */ + if(item == (struct collection_item *)(NULL)) { + printf("Expected success but got NULL.\n"); + destroy_collection(ini_config); + return -1; + } + + printf("Convert item to bool.\n"); + + error = 0; + logical = get_bool_config_value(item, 1, &error); + if(error) { + printf("Expect success got error %d.\n",error); + destroy_collection(ini_config); + return error; + } + + if(logical) { + printf("Expected false but got true - bad.\n"); + return -1; + } + + printf("In the files it is FALSE so we got false.\n"); + + printf("Get binary item\n"); + + item = (struct collection_item *)(NULL); + error = get_config_item("domains/EXAMPLE.COM","binary_test", ini_config, &item); + if(error) { + printf("Expected success but got error! %d\n",error); + destroy_collection(ini_config); + return error; + } + + /* Item should be found */ + if(item == (struct collection_item *)(NULL)) { + printf("Expected success but got NULL.\n"); + destroy_collection(ini_config); + return -1; + } + + debug_item(item); + + error = 0; + binary = get_bin_config_value(item, &length, &error); + if(error) { + printf("Expect success got error %d.\n",error); + destroy_collection(ini_config); + return error; + } + + printf("Binary value (expect 123) = "); + for(i=0;i<length;i++) { + printf("%d",*((unsigned char*)(binary)+i)); + } + printf("\n"); + + free_bin_config_value(binary); + + printf("Get string array item\n"); + + item = (struct collection_item *)(NULL); + error = get_config_item("domains","domainsorder", ini_config, &item); + if(error) { + printf("Expected success but got error! %d\n",error); + destroy_collection(ini_config); + return error; + } + + /* Item should be found */ + if(item == (struct collection_item *)(NULL)) { + printf("Expected success but got NULL.\n"); + destroy_collection(ini_config); + return -1; + } + + debug_item(item); + + printf("Get str array without size.\n"); + + error = 0; + strarray = get_string_config_array(item, ",", NULL, &error); + if(error) { + printf("Expect success got error %d.\n",error); + destroy_collection(ini_config); + return error; + } + + /* Can be used with this cycle */ + strptr = strarray; + while(*strptr != NULL) { + printf("[%s]\n",*strptr); + strptr++; + } + + free_string_config_array(strarray); + + printf("Get str array with size.\n"); + + error = 0; + size = 0; + strarray = get_string_config_array(item, ",", &size, &error); + if(error) { + printf("Expect success got error %d.\n",error); + destroy_collection(ini_config); + return error; + } + + /* Can be used with this cycle */ + for(i=0;i<size;i++) printf("[%s]\n",*(strarray + i)); + + free_string_config_array(strarray); + + printf("Get long array item\n"); + + item = (struct collection_item *)(NULL); + error = get_config_item("domains/EXAMPLE.COM","long_array", ini_config, &item); + if(error) { + printf("Expected success but got error! %d\n",error); + destroy_collection(ini_config); + return error; + } + + /* Item should be found */ + if(item == (struct collection_item *)(NULL)) { + printf("Expected success but got NULL.\n"); + destroy_collection(ini_config); + return -1; + } + + debug_item(item); + + error = 0; + size = 0; /* Here size is not optional!!! */ + array = get_long_config_array(item, &size, &error); + if(error) { + printf("Expect success got error %d.\n",error); + destroy_collection(ini_config); + return error; + } + + /* Can be used with this cycle */ + for(i=0;i<size;i++) printf("%d\n",*(array + i)); + + free_long_config_array(array); + + printf("Done with get test!\n"); + return EOK; +} + +int main() +{ + int error; + + if((error=basic_test()) || + (error=single_file()) || + (error=real_test(NULL)) || + /* This should result in merged configuration */ + (error=real_test("./ini/ini.conf")) || + (error= get_test())) { + printf("Test failed! Error %d.\n",error); + return -1; + } + printf("Success!\n"); + return 0; +} |