summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ini/ini_configobj.c456
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;
+
+}