diff options
-rw-r--r-- | ini/ini.d/foo.conf | 12 | ||||
-rw-r--r-- | ini/ini_configobj.h | 87 | ||||
-rw-r--r-- | ini/ini_defines.h | 5 | ||||
-rw-r--r-- | ini/ini_parse.c | 114 | ||||
-rw-r--r-- | ini/ini_parse_ut.c | 203 |
5 files changed, 410 insertions, 11 deletions
diff --git a/ini/ini.d/foo.conf b/ini/ini.d/foo.conf new file mode 100644 index 0000000..44d56a1 --- /dev/null +++ b/ini/ini.d/foo.conf @@ -0,0 +1,12 @@ +#Hoho section +[hoho] +#Hoho value +val= hoho +#End of hoho +#Start of section +[foo] +#First value +bar = first value +#Second value +bar = second value +#End of section diff --git a/ini/ini_configobj.h b/ini/ini_configobj.h index 21198db..88a8704 100644 --- a/ini/ini_configobj.h +++ b/ini/ini_configobj.h @@ -29,6 +29,7 @@ #include <stdio.h> #include "simplebuffer.h" + /** * @defgroup errorlevel Error tolerance constants * @@ -79,6 +80,92 @@ * @} */ + +/** + * @defgroup collisionflags Flags that define collision resolution logic. + * + * @{ + */ + +/** + * @defgroup onesecvalue Colliding values come from one section + * + * Flags that define collision resolution logic for values in + * the same section. + * These flags should be used during parsing to handle duplicate + * keys in the same section of the ini file. + * + * @{ + */ + +/** @brief Value with same key is ovewritten */ +#define INI_MV1S_OVERWRITE 0x0000 +/** @brief Collision causes error */ +#define INI_MV1S_ERROR 0x0001 +/** @brief Second value is discarded */ +#define INI_MV1S_PRESERVE 0x0002 +/** @brief Duplicates are allowed */ +#define INI_MV1S_ALLOW 0x0003 + +/** + * @} + */ + +/** + * @defgroup twosecvalue Colliding values come from two sections + * + * Flags that define collision resolution logic between two values + * that come from two sections with the same name. + * These flags should be used during parsing to handle duplicate + * keys coming from the same section scattered across the ini file. + * These flags also can be used to specify the rules of merging + * values that come from two files separate configuration files. + * + * @{ + */ +/** @brief Value with same key is ovewritten */ +#define INI_MV2S_OVERWRITE 0x0000 +/** @brief Collision causes error */ +#define INI_MV2S_ERROR 0x0010 +/** @brief Second value is discarded */ +#define INI_MV2S_PRESERVE 0x0020 +/** @brief Duplicates are allowed */ +#define INI_MV2S_ALLOW 0x0030 + +/** + * @} + */ + +/** + * @defgroup mergesec Collision in two sections + * + * Flags that define collision resolution logic between two sections. + * These flags should be used during parsing to handle duplicate + * sections scattered across the ini file. + * These flags also can be used to specify the rules of merging + * sections that come from two separate configuration files. + * + * @{ + */ +/** @brief Sections are merged */ +#define INI_MS_MERGE 0x0000 +/** @brief Collision causes error */ +#define INI_MS_ERROR 0x0100 +/** @brief First section is discarded */ +#define INI_MS_OVERWRITE 0x0200 +/** @brief Second section is discarded */ +#define INI_MS_PRESERVE 0x0300 +/** @brief Duplicates are allowed */ +#define INI_MS_ALLOW 0x0400 + +/** + * @} + */ +/** + * @} + */ + + /** * @defgroup structures Structures * @{ diff --git a/ini/ini_defines.h b/ini/ini_defines.h index 1d03835..b861f0e 100644 --- a/ini/ini_defines.h +++ b/ini/ini_defines.h @@ -100,6 +100,11 @@ #define INI_FAMILY_VALIDATION 1 #define INI_FAMILY_GRAMMAR 2 +#define INI_MV1S_MASK 0x000F /* Merge values options mask + * for one section */ +#define INI_MV2S_MASK 0x00F0 /* Merge values options mask + * for two sections. */ +#define INI_MS_MASK 0x0F00 /* Merge section options mask */ /* Different error string functions can be passed as callbacks */ diff --git a/ini/ini_parse.c b/ini/ini_parse.c index c92a163..acdb608 100644 --- a/ini/ini_parse.c +++ b/ini/ini_parse.c @@ -29,21 +29,36 @@ #include "config.h" #include "trace.h" #include "ini_defines.h" -#include "ini_config.h" #include "ini_valueobj.h" #include "ini_config_priv.h" +#include "ini_configobj.h" #include "collection.h" #include "collection_queue.h" #define INI_WARNING 0xA0000000 /* Warning bit */ +/* This constant belongs to ini_defines.h. Move from ini_config - TBD */ +#define COL_CLASS_INI_BASE 20000 +#define COL_CLASS_INI_SECTION COL_CLASS_INI_BASE + 1 +/** + * @brief Name of the default section. + * + * This is the name of the implied section where orphan key-value + * pairs will be put. + */ +#define INI_DEFAULT_SECTION "default" + + struct parser_obj { /* Externally passed and saved data */ FILE *file; struct collection_item *top; struct collection_item *el; const char *filename; + /* Level of error reporting */ int error_level; + /* Collistion flags */ + uint32_t collision_flags; /* Wrapping boundary */ uint32_t boundary; /* Action queue */ @@ -122,6 +137,7 @@ int parser_create(FILE *file, const char *config_filename, struct collection_item *ini_config, int error_level, + uint32_t collision_flags, struct collection_item *error_list, uint32_t boundary, struct parser_obj **po) @@ -160,6 +176,7 @@ int parser_create(FILE *file, new_po->el = error_list; new_po->filename = config_filename; new_po->error_level = error_level; + new_po->collision_flags = collision_flags; new_po->boundary = boundary; /* Initialize internal varibles */ @@ -287,6 +304,12 @@ static int complete_value_processing(struct parser_obj *po) { int error = EOK; struct value_obj *vo = NULL; + struct value_obj *vo_old = NULL; + unsigned insertmode; + uint32_t mergemode; + int suppress = 0; + int doinsert = 0; + struct collection_item *item = NULL; TRACE_FLOW_ENTRY(); @@ -322,17 +345,85 @@ static int complete_value_processing(struct parser_obj *po) po->raw_lines = NULL; po->raw_lengths = NULL; + mergemode = po->collision_flags & INI_MV1S_MASK; + + switch (mergemode) { + case INI_MV1S_ERROR: insertmode = COL_INSERT_DUPERROR; + doinsert = 1; + break; + case INI_MV1S_PRESERVE: insertmode = COL_INSERT_DUPERROR; + doinsert = 1; + suppress = 1; + break; + case INI_MV1S_ALLOW: insertmode = COL_INSERT_NOCHECK; + doinsert = 1; + break; + case INI_MV1S_OVERWRITE: /* Special handling */ + default: + break; + } - /* Add value to collection */ - error = col_add_binary_property(po->sec, - NULL, - po->key, - &vo, - sizeof(struct value_obj *)); - if (error) { - TRACE_ERROR_NUMBER("Failed to add value object to the section", error); - value_destroy(vo); - return error; + /* Do not insert but search for dups first */ + if (!doinsert) { + TRACE_INFO_STRING("Ovewrite mode. Lokking for:", po->key); + + error = col_get_item(po->sec, + po->key, + COL_TYPE_BINARY, + COL_TRAVERSE_DEFAULT, + &item); + + if (error) { + TRACE_ERROR_NUMBER("Failed searching for dup", error); + return error; + } + + /* Check if there is a dup */ + if (item) { + /* Dup exists - update it */ + vo_old = *((struct value_obj **)(col_get_item_data(item))); + error = col_modify_binary_item(item, + NULL, + &vo, + sizeof(struct value_obj *)); + if (error) { + TRACE_ERROR_NUMBER("Failed updating the value", error); + return error; + } + /* If we failed to update it is better to leak then crash, + * so desctroy original value only on the successful update. + */ + value_destroy(vo_old); + } + else { + /* No dup found so we can insert with no check */ + doinsert = 1; + insertmode = COL_INSERT_NOCHECK; + } + } + + if (doinsert) { + /* Add value to collection */ + error = col_insert_binary_property(po->sec, + NULL, + COL_DSP_END, + NULL, + 0, + insertmode, + po->key, + &vo, + sizeof(struct value_obj *)); + if (error) { + if ((suppress) && (error == EEXIST)) { + TRACE_INFO_STRING("Preseved exisitng value", po->key); + value_destroy(vo); + } + else { + TRACE_ERROR_NUMBER("Failed to add value object to the section", error); + value_destroy(vo); + return error; + } + } } free(po->key); @@ -970,6 +1061,7 @@ int ini_config_parse(struct ini_cfgfile *file_ctx, file_ctx->filename, ini_config->cfg, file_ctx->error_level, + file_ctx->collision_flags, file_ctx->error_list, ini_config->boundary, &po); diff --git a/ini/ini_parse_ut.c b/ini/ini_parse_ut.c index cc37c21..eed4649 100644 --- a/ini/ini_parse_ut.c +++ b/ini/ini_parse_ut.c @@ -231,6 +231,208 @@ int read_again_test(void) return error; } +int create_expect(char *checkname) +{ + FILE *ff; + int error = EOK; + + errno = 0; + ff = fopen(checkname, "w"); + if(!ff) { + error = errno; + printf("Failed to open file for writing. Error %d.\n", error); + return error; + } + + /* Ovewrite */ + fprintf(ff,"#Hoho section\n"); + fprintf(ff,"[hoho]\n"); + fprintf(ff,"#Hoho value\n"); + fprintf(ff,"val = hoho\n"); + fprintf(ff,"#End of hoho\n"); + fprintf(ff,"#Start of section\n"); + fprintf(ff,"[foo]\n"); + fprintf(ff,"#Second value\n"); + fprintf(ff,"bar = second value\n"); + fprintf(ff,"#End of section\n"); + /* Error */ + fprintf(ff,"#Hoho section\n"); + fprintf(ff,"[hoho]\n"); + fprintf(ff,"#Hoho value\n"); + fprintf(ff,"val = hoho\n"); + /* No "#End of hoho" line is expected due to error */ + /* Preserve */ + fprintf(ff,"#Hoho section\n"); + fprintf(ff,"[hoho]\n"); + fprintf(ff,"#Hoho value\n"); + fprintf(ff,"val = hoho\n"); + fprintf(ff,"#End of hoho\n"); + fprintf(ff,"#Start of section\n"); + fprintf(ff,"[foo]\n"); + fprintf(ff,"#First value\n"); + fprintf(ff,"bar = first value\n"); + fprintf(ff,"#End of section\n"); + /* Allow */ + fprintf(ff,"#Hoho section\n"); + fprintf(ff,"[hoho]\n"); + fprintf(ff,"#Hoho value\n"); + fprintf(ff,"val = hoho\n"); + fprintf(ff,"#End of hoho\n"); + fprintf(ff,"#Start of section\n"); + fprintf(ff,"[foo]\n"); + fprintf(ff,"#First value\n"); + fprintf(ff,"bar = first value\n"); + fprintf(ff,"#Second value\n"); + fprintf(ff,"bar = second value\n"); + fprintf(ff,"#End of section\n"); + + fclose(ff); + + return EOK; +} + + + +/* Check merge modes */ +int merge_values_test(void) +{ + int error = EOK; + int i; + struct ini_cfgfile *file_ctx = NULL; + FILE *ff = NULL; + struct ini_cfgobj *ini_config = NULL; + char **error_list = NULL; + struct simplebuffer *sbobj = NULL; + uint32_t left = 0; + uint32_t mflags[] = { INI_MV1S_OVERWRITE, + INI_MV1S_ERROR, + INI_MV1S_PRESERVE, + INI_MV1S_ALLOW }; + + const char *mstr[] = { "OVERWRITE", + "ERROR", + "PRESERVE", + "ALLOW" }; + + char filename[PATH_MAX]; + char resname[PATH_MAX]; + char checkname[PATH_MAX]; + char command[PATH_MAX * 3]; + char *srcdir; + + srcdir = getenv("srcdir"); + sprintf(filename, "%s/ini/ini.d/foo.conf", (srcdir == NULL) ? "." : srcdir); + sprintf(resname, "%s/merge.conf", (srcdir == NULL) ? "." : srcdir); + sprintf(checkname, "%s/expect.conf", (srcdir == NULL) ? "." : srcdir); + + error = simplebuffer_alloc(&sbobj); + if (error) { + TRACE_ERROR_NUMBER("Failed to allocate dynamic string.", error); + return error; + } + + for (i = 0; i < 4; i++) { + + INIOUT(printf("<==== Testing mode %s ====>\n", mstr[i])); + + /* Create config collection */ + error = ini_config_create(&ini_config); + if (error) { + printf("Failed to create collection. Error %d.\n", error); + simplebuffer_free(sbobj); + return error; + } + + error = ini_config_file_open(filename, + INI_STOP_ON_ANY, + mflags[i], + 0, /* TBD */ + &file_ctx); + if (error) { + printf("Failed to open file for reading. Error %d.\n", error); + ini_config_destroy(ini_config); + simplebuffer_free(sbobj); + return error; + } + + error = ini_config_parse(file_ctx, + ini_config); + if (error) { + INIOUT(printf("Failed to parse configuration. Error %d.\n", error)); + + if (ini_config_error_count(file_ctx)) { + INIOUT(printf("Errors detected while parsing: %s\n", + ini_config_get_filename(file_ctx))); + ini_config_get_errors(file_ctx, &error_list); + INIOUT(ini_print_errors(stdout, error_list)); + ini_config_free_errors(error_list); + } + + if ((mflags[i] != INI_MV1S_ERROR) || + ((mflags[i] = INI_MV1S_ERROR) && (error != EEXIST))) { + printf("This is unexpected error %d in mode %d\n", error, mflags[i]); + ini_config_destroy(ini_config); + simplebuffer_free(sbobj); + ini_config_file_close(file_ctx); + return error; + } + /* We do not return here intentionally */ + } + + ini_config_file_close(file_ctx); + + INIOUT(col_debug_collection(ini_config->cfg, COL_TRAVERSE_DEFAULT)); + + error = ini_config_serialize(ini_config, sbobj); + if (error != EOK) { + printf("Failed to serialize configuration. Error %d.\n", error); + ini_config_destroy(ini_config); + simplebuffer_free(sbobj); + return error; + } + + ini_config_destroy(ini_config); + } + + errno = 0; + ff = fopen(resname, "w"); + if(!ff) { + error = errno; + printf("Failed to open file for writing. Error %d.\n", error); + simplebuffer_free(sbobj); + return error; + } + + /* Save */ + left = simplebuffer_get_len(sbobj); + while (left > 0) { + error = simplebuffer_write(fileno(ff), sbobj, &left); + if (error) { + printf("Failed to write back the configuration %d.\n", error); + simplebuffer_free(sbobj); + fclose(ff); + return error; + } + } + + simplebuffer_free(sbobj); + fclose(ff); + + error = create_expect(checkname); + if (error) { + printf("Failed to create file with expected contents %d.\n", error); + return error; + } + + sprintf(command,"diff -q %s %s", resname, checkname); + error = system(command); + INIOUT(printf("Comparison of %s %s returned: %d\n", + resname, checkname, error)); + + return error; + + +} /* Main function of the unit test */ int main(int argc, char *argv[]) @@ -238,6 +440,7 @@ int main(int argc, char *argv[]) int error = 0; test_fn tests[] = { read_save_test, read_again_test, + merge_values_test, NULL }; test_fn t; int i = 0; |