diff options
Diffstat (limited to 'ini/ini_configobj.c')
-rw-r--r-- | ini/ini_configobj.c | 456 |
1 files changed, 456 insertions, 0 deletions
diff --git a/ini/ini_configobj.c b/ini/ini_configobj.c index 5e5e40b..d8583a2 100644 --- a/ini/ini_configobj.c +++ b/ini/ini_configobj.c @@ -30,11 +30,19 @@ #include "ini_config_priv.h" #include "ini_defines.h" #include "ini_valueobj.h" +#include "ini_configobj.h" /* This constant belongs to ini_defines.h. Move from ini_config - TBD */ #define COL_CLASS_INI_BASE 20000 #define COL_CLASS_INI_CONFIG COL_CLASS_INI_BASE + 0 +struct merge_data { + struct collection_item *ci; + uint32_t flags; + int error; + int found; +}; + /* Callback */ void ini_cleanup_cb(const char *property, int property_len, @@ -254,3 +262,451 @@ int ini_config_copy(struct ini_cfgobj *ini_config, TRACE_FLOW_EXIT(); return error; } + +/* Callback to process merging of the sections */ +static int merge_section_handler(const char *property, + int property_len, + int type, + void *data, + int length, + void *custom_data, + int *dummy) +{ + int error = EOK; + struct value_obj *vo = NULL; + struct value_obj *new_vo = NULL; + struct value_obj *vo_old = NULL; + struct merge_data *passed_data; + struct collection_item *acceptor = NULL; + struct collection_item *item = NULL; + unsigned insertmode; + uint32_t mergemode; + int suppress = 0; + int doinsert = 0; + + TRACE_FLOW_ENTRY(); + + if ((type != COL_TYPE_BINARY) || + ((type == COL_TYPE_BINARY) && + (strncmp(property, INI_SECTION_KEY, + sizeof(INI_SECTION_KEY)) == 0))) { + /* Skip items we do not care about */ + TRACE_FLOW_EXIT(); + return EOK; + } + + /* Get value */ + vo = *((struct value_obj **)(data)); + + /* Copy it */ + error = value_copy(vo, &new_vo); + if (error) { + TRACE_ERROR_NUMBER("Failed to copy value", error); + return error; + } + + passed_data = (struct merge_data *)(custom_data); + acceptor = passed_data->ci; + mergemode = passed_data->flags & INI_MV2S_MASK; + + switch (mergemode) { + case INI_MV2S_ERROR: insertmode = COL_INSERT_DUPERROR; + doinsert = 1; + break; + case INI_MV2S_PRESERVE: insertmode = COL_INSERT_DUPERROR; + doinsert = 1; + suppress = 1; + break; + case INI_MV2S_ALLOW: insertmode = COL_INSERT_NOCHECK; + doinsert = 1; + break; + case INI_MV2S_OVERWRITE: /* Special handling */ + case INI_MV2S_DETECT: + default: + break; + } + + /* Do not insert but search for dups first */ + if (!doinsert) { + TRACE_INFO_STRING("Overwrite mode. Looking for:", + property); + + error = col_get_item(acceptor, + property, + COL_TYPE_BINARY, + COL_TRAVERSE_DEFAULT, + &item); + + if (error) { + TRACE_ERROR_NUMBER("Failed searching for dup", error); + value_destroy(new_vo); + return error; + } + + /* Check if there is a dup */ + if (item) { + /* Check if we are in the detect mode */ + if (mergemode == INI_MV2S_DETECT) { + passed_data->error = EEXIST; + doinsert = 1; + insertmode = COL_INSERT_NOCHECK; + } + else { + + /* Dup exists - update it */ + vo_old = *((struct value_obj **)(col_get_item_data(item))); + error = col_modify_binary_item(item, + NULL, + &new_vo, + sizeof(struct value_obj *)); + if (error) { + TRACE_ERROR_NUMBER("Failed updating the value", error); + value_destroy(new_vo); + return error; + } + + /* If we failed to update it is better to leak then crash, + * so destroy 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(acceptor, + NULL, + COL_DSP_END, + NULL, + 0, + insertmode, + property, + &new_vo, + sizeof(struct value_obj *)); + if (error) { + value_destroy(new_vo); + + if ((suppress) && (error == EEXIST)) { + TRACE_INFO_STRING("Preseved exisitng value", + property); + } + else { + /* Check if this is a critical error or not */ + if ((mergemode == INI_MV1S_ERROR) && (error == EEXIST)) { + TRACE_ERROR_NUMBER("Failed to add value object " + "to the section", error); + passed_data->error = EEXIST; + *dummy = 1; + } + else { + TRACE_ERROR_NUMBER("Failed to add value object" + " to the section", error); + return error; + } + } + } + } + + TRACE_FLOW_EXIT(); + return error; +} + + +/* Internal function to merge two configs */ +static int merge_two_sections(struct collection_item *acceptor, + struct collection_item *donor, + uint32_t flags) +{ + int error = EOK; + struct merge_data data; + + TRACE_FLOW_ENTRY(); + + data.ci = acceptor; + data.flags = flags; + data.error = 0; + data.found = 0; + + error = col_traverse_collection(donor, + COL_TRAVERSE_ONELEVEL, + merge_section_handler, + (void *)(&data)); + if (error) { + TRACE_ERROR_NUMBER("Merge values failed", error); + return error; + } + + TRACE_FLOW_EXIT(); + return data.error; +} + + + +/* Callback to process the accepting config */ +static int acceptor_handler(const char *property, + int property_len, + int type, + void *data, + int length, + void *custom_data, + int *dummy) +{ + int error = EOK; + struct merge_data *passed_data; + struct collection_item *acceptor = NULL; + struct collection_item *donor = NULL; + uint32_t mergemode; + + TRACE_FLOW_ENTRY(); + + passed_data = (struct merge_data *)(custom_data); + passed_data->found = 1; + + donor = passed_data->ci; + acceptor = *((struct collection_item **)(data)); + + mergemode = passed_data->flags & INI_MS_MASK; + + switch (mergemode) { + case INI_MS_ERROR: /* Report error and return */ + TRACE_INFO_STRING("Error ", + "duplicate section"); + passed_data->error = EEXIST; + break; + + case INI_MS_PRESERVE: /* Preserve what we have */ + TRACE_INFO_STRING("Preserve mode", ""); + break; + + case INI_MS_OVERWRITE: /* Empty existing section */ + TRACE_INFO_STRING("Ovewrite mode", ""); + error = empty_section(acceptor); + if (error) { + TRACE_ERROR_NUMBER("Failed to " + "empty section", + error); + return error; + } + error = merge_two_sections(acceptor, + donor, + passed_data->flags); + if (error) { + TRACE_ERROR_NUMBER("Failed to merge " + "sections", error); + return error; + } + break; + + case INI_MS_DETECT: /* Detect mode */ + TRACE_INFO_STRING("Detect mode", ""); + passed_data->error = EEXIST; + passed_data->found = 0; + break; + + case INI_MS_MERGE: /* Merge */ + default: TRACE_INFO_STRING("Merge mode", ""); + error = merge_two_sections(acceptor, + donor, + passed_data->flags); + if (error) { + if (error != EEXIST) { + TRACE_ERROR_NUMBER("Failed to merge " + "sections", error); + return error; + } + passed_data->error = error; + } + break; + } + + *dummy = 1; + TRACE_FLOW_EXIT(); + return EOK; +} + +/* Callback to process the donating config */ +static int donor_handler(const char *property, + int property_len, + int type, + void *data, + int length, + void *custom_data, + int *dummy) +{ + int error = EOK; + struct merge_data *passed_data; + struct merge_data acceptor_data; + struct collection_item *new_ci = NULL; + + TRACE_FLOW_ENTRY(); + + passed_data = (struct merge_data *)(custom_data); + + /* All section are subcollections */ + if(type == COL_TYPE_COLLECTIONREF) { + + acceptor_data.flags = passed_data->flags; + acceptor_data.ci = *((struct collection_item **)(data)); + acceptor_data.error = 0; + acceptor_data.found = 0; + + /* Try to resolve collision only non ALLOW modes */ + if (!(acceptor_data.flags & INI_MS_ALLOW)) { + error = col_get_item_and_do(passed_data->ci, + property, + COL_TYPE_COLLECTIONREF, + COL_TRAVERSE_ONELEVEL, + acceptor_handler, + (void *)(&acceptor_data)); + if (error) { + TRACE_ERROR_NUMBER("Critical error", error); + return error; + } + } + + /* Was duplicate found ? */ + if (acceptor_data.found) { + /* Check for logical error. It can be only EEXIST */ + if (acceptor_data.error) { + /* Save error anyway */ + passed_data->error = acceptor_data.error; + /* If it is section DETECT or MERGE+DETECT */ + if ((passed_data->flags & INI_MS_DETECT) || + ((passed_data->flags & INI_MS_MERGE) && + (passed_data->flags & INI_MV2S_DETECT))) { + TRACE_INFO_NUMBER("Non-critical error", + acceptor_data.error); + } + else { + /* In any other mode we need to stop */ + TRACE_INFO_NUMBER("Merge error detected", + acceptor_data.error); + /* Force stop */ + *dummy = 1; + } + } + } + else { + /* Not found? Then create a copy... */ + error = col_copy_collection_with_cb(&new_ci, + acceptor_data.ci, + NULL, + COL_COPY_NORMAL, + ini_copy_cb, + NULL); + if (error) { + TRACE_ERROR_NUMBER("Failed to copy collection", error); + return error; + } + + /* ... and embed into the existing collection */ + error = col_add_collection_to_collection(passed_data->ci, + NULL, + NULL, + new_ci, + COL_ADD_MODE_EMBED); + if (error) { + TRACE_ERROR_NUMBER("Failed to copy collection", error); + col_destroy_collection(new_ci); + return error; + } + } + } + + TRACE_FLOW_EXIT(); + return EOK; +} + + +/* Internal function to merge two configs */ +static int merge_configs(struct ini_cfgobj *first, + struct ini_cfgobj *second, + uint32_t collision_flags) +{ + int error = EOK; + struct merge_data data; + + TRACE_FLOW_ENTRY(); + + data.ci = first->cfg; + data.flags = collision_flags; + data.error = 0; + data.found = 0; + + error = col_traverse_collection(second->cfg, + COL_TRAVERSE_ONELEVEL, + donor_handler, + (void *)(&data)); + if (error) { + TRACE_ERROR_NUMBER("Merge failed", error); + return error; + } + + /* If boundaries are different re-align the values */ + if (first->boundary != second->boundary) { + + error = ini_config_set_wrap(first, first->boundary); + if (error) { + TRACE_ERROR_NUMBER("Failed to re-align", error); + return error; + } + } + + TRACE_FLOW_EXIT(); + return error; +} + + +/* Merge two configurations together creating a new one */ +int ini_config_merge(struct ini_cfgobj *first, + struct ini_cfgobj *second, + uint32_t collision_flags, + struct ini_cfgobj **result) +{ + int error = EOK; + struct ini_cfgobj *new_co = NULL; + + TRACE_FLOW_ENTRY(); + + /* Check input params */ + if ((!first) || + (!second) || + (!result)) { + TRACE_ERROR_NUMBER("Invalid argument", EINVAL); + return EINVAL; + } + + /* Check collision flags */ + if (!valid_collision_flags(collision_flags)) { + TRACE_ERROR_NUMBER("Invalid flags.", EINVAL); + return EINVAL; + } + + /* Create a copy */ + /* TBD - create a COMPACT copy */ + error = ini_config_copy(first, &new_co); + if (error) { + TRACE_ERROR_NUMBER("Failed to copy collection", error); + return error; + } + + /* Merge configs */ + error = merge_configs(new_co, second, collision_flags); + if (error) { + TRACE_ERROR_NUMBER("Failed to copy collection", error); + ini_config_destroy(new_co); + return error; + } + + *result = new_co; + + TRACE_FLOW_EXIT(); + return error; + +} |