From c590b522cf1927fb1c7a9b45e5a0e648f620d242 Mon Sep 17 00:00:00 2001
From: Dmitri Pal <dpal@dpal.csb>
Date: Sun, 7 Dec 2014 17:10:07 -0500
Subject: [PATCH 08/16] [INI] New interface to modify configuration

This patch adds a new interface to modify configuration.
---
 ini/ini_configmod.c    | 1533 ++++++++++++++++++++++++++++++++++++++++++++++++
 ini/ini_configmod.h    |  744 +++++++++++++++++++++++
 ini/ini_configmod_ut.c | 1152 ++++++++++++++++++++++++++++++++++++
 3 files changed, 3429 insertions(+), 0 deletions(-)
 create mode 100644 ini/ini_configmod.c
 create mode 100644 ini/ini_configmod.h
 create mode 100644 ini/ini_configmod_ut.c

diff --git a/ini/ini_configmod.c b/ini/ini_configmod.c
new file mode 100644
index 0000000000000000000000000000000000000000..11c41358264d93842886f597ea5cd2958627f148
--- /dev/null
+++ b/ini/ini_configmod.c
@@ -0,0 +1,1533 @@
+/*
+    INI LIBRARY
+
+    Implementation of the modification interface.
+
+    Copyright (C) Dmitri Pal <dpal@redhat.com> 2014
+
+    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/>.
+*/
+
+#define _GNU_SOURCE /* for asprintf */
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include "config.h"
+#include "trace.h"
+#include "ref_array.h"
+#include "simplebuffer.h"
+#include "collection.h"
+#include "ini_comment.h"
+#include "ini_defines.h"
+#include "ini_valueobj.h"
+#include "ini_configobj.h"
+#include "ini_config_priv.h"
+#include "ini_configmod.h"
+
+/* Which kind of search we should use? */
+#define EXACT(a) ((a == INI_VA_MOD_E) || (a == INI_VA_MODADD_E)) ? 1 : 0
+
+static void cb(const char *property,
+               int property_len,
+               int type,
+               void *data,
+               int length,
+               void *ext_data);
+
+
+/* Delete value by key or position */
+int ini_config_delete_value(struct ini_cfgobj *ini_config,
+                            const char *section,
+                            int position,
+                            const char *key,
+                            int idx)
+{
+    int error = EOK;
+    struct value_obj *vo = NULL;
+    struct collection_item *item = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    /* Check arguments */
+    if (!ini_config) {
+        TRACE_ERROR_STRING("Invalid argument","ini_config");
+        return EINVAL;
+    }
+
+    if (!section) {
+        TRACE_ERROR_STRING("Invalid argument","section");
+        return EINVAL;
+    }
+
+    if (!key) {
+        TRACE_ERROR_STRING("Invalid argument","key");
+        return EINVAL;
+    }
+
+    if (idx < 0) {
+        TRACE_ERROR_STRING("Invalid argument","idx");
+        return EINVAL;
+    }
+
+    error = col_extract_item(ini_config->cfg,
+                             section,
+                             position,
+                             key,
+                             idx,
+                             COL_TYPE_ANY,
+                             &item);
+    if (error) {
+        TRACE_ERROR_NUMBER("Item not found or error",
+                            error);
+        return error;
+    }
+
+    vo = *((struct value_obj **)(col_get_item_data(item)));
+    value_destroy(vo);
+
+    col_delete_item(item);
+
+    TRACE_FLOW_EXIT();
+    return error;
+}
+
+
+/* Updates a comment for value that is found by seaching for a specific key */
+int ini_config_update_comment(struct ini_cfgobj *ini_config,
+                              const char *section,
+                              const char *key,
+                              const char *comments[],
+                              size_t count_comment,
+                              int idx)
+{
+    int error = EOK;
+    struct ini_comment *ic = NULL;
+    struct value_obj *vo = NULL;
+    struct collection_item *item = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    /* Check arguments */
+    if (!ini_config) {
+        TRACE_ERROR_STRING("Invalid argument","ini_config");
+        return EINVAL;
+    }
+
+    if (!section) {
+        TRACE_ERROR_STRING("Invalid argument","section");
+        return EINVAL;
+    }
+
+    if (!key) {
+        TRACE_ERROR_STRING("Invalid argument","key");
+        return EINVAL;
+    }
+
+    if (idx < 0) {
+        TRACE_ERROR_STRING("Invalid argument","idx");
+        return EINVAL;
+    }
+
+    /* Look for the exact item */
+    error = col_get_dup_item(ini_config->cfg,
+                             section,
+                             key,
+                             COL_TYPE_ANY,
+                             idx,
+                             1,
+                             &item);
+
+    if (error) {
+        TRACE_ERROR_NUMBER("Item not found or error",
+                            error);
+        return error;
+    }
+
+    /* If item not found return error */
+    if (!item) {
+        TRACE_ERROR_NUMBER("Item not found.", ENOENT);
+        return ENOENT;
+    }
+
+    /* Build comment */
+    if (comments) {
+        error = ini_comment_construct(comments,
+                                      count_comment,
+                                      &ic);
+        if (error) {
+            TRACE_ERROR_NUMBER("Failed to construct comment", error);
+            return error;
+        }
+    }
+
+    vo = *((struct value_obj **)(col_get_item_data(item)));
+
+    /* Replace comment with the new one. Old one is freed by the function */
+    error = value_put_comment(vo, ic);
+    if (error) {
+        TRACE_ERROR_NUMBER("Faile to update comment.",
+                            error);
+        ini_comment_destroy(ic);
+        return error;
+    }
+
+    TRACE_FLOW_EXIT();
+    return EOK;
+}
+
+/* Add or modify a value that stores array of integers */
+int ini_config_add_int_arr_value(struct ini_cfgobj *ini_config,
+                                 const char *section,
+                                 const char *key,
+                                 int *value_int_arr,
+                                 size_t count_int,
+                                 char sep,
+                                 const char *comments[],
+                                 size_t count_comment,
+                                 int border,
+                                 int position,
+                                 const char *other_key,
+                                 int idx,
+                                 enum INI_VA flags)
+{
+    int error = EOK;
+    int ret = 0;
+    char **str_arr = NULL;
+    size_t i = 0;
+    size_t j = 0;
+
+    TRACE_FLOW_ENTRY();
+
+    if (!count_int) {
+        TRACE_ERROR_STRING("Invalid argument","count_int");
+        return EINVAL;
+    }
+
+    str_arr = malloc(count_int * sizeof(char *));
+    if (!str_arr) {
+        TRACE_ERROR_STRING("Failed to allocate memory for string array",
+                           ENOMEM);
+        return ENOMEM;
+    }
+
+    for (i = 0; i < count_int; i++) {
+        ret = asprintf(&str_arr[i], "%d", value_int_arr[i]);
+        if (ret == -1) {
+            TRACE_ERROR_NUMBER("Asprintf failed.", ret);
+            /* The main reason is propbaly memory allocation */
+            for (j = 0; j < i; j++) free(str_arr[j]);
+            free(str_arr);
+            return ENOMEM;
+        }
+    }
+
+    error = ini_config_add_str_arr_value(ini_config,
+                                         section,
+                                         key,
+                                         (const char **)str_arr,
+                                         count_int,
+                                         sep,
+                                         comments,
+                                         count_comment,
+                                         border,
+                                         position,
+                                         other_key,
+                                         idx,
+                                         flags);
+
+    for (i = 0; i < count_int; i++) free(str_arr[i]);
+    free(str_arr);
+
+    TRACE_FLOW_RETURN(error);
+    return error;
+}
+
+/* Add or modify a value that stores array of long ints */
+int ini_config_add_long_arr_value(struct ini_cfgobj *ini_config,
+                                  const char *section,
+                                  const char *key,
+                                  long *value_long_arr,
+                                  size_t count_long,
+                                  char sep,
+                                  const char *comments[],
+                                  size_t count_comment,
+                                  int border,
+                                  int position,
+                                  const char *other_key,
+                                  int idx,
+                                  enum INI_VA flags)
+{
+    int error = EOK;
+    int ret = 0;
+    char **str_arr = NULL;
+    size_t i = 0;
+    size_t j = 0;
+
+    TRACE_FLOW_ENTRY();
+
+    if (!count_long) {
+        TRACE_ERROR_STRING("Invalid argument","count_long");
+        return EINVAL;
+    }
+
+    str_arr = malloc(count_long * sizeof(char *));
+    if (!str_arr) {
+        TRACE_ERROR_STRING("Failed to allocate memory for string array",
+                           ENOMEM);
+        return ENOMEM;
+    }
+
+    for (i = 0; i < count_long; i++) {
+        ret = asprintf(&str_arr[i], "%ld", value_long_arr[i]);
+        if (ret == -1) {
+            TRACE_ERROR_NUMBER("Asprintf failed.", ret);
+            /* The main reason is propbaly memory allocation */
+            for (j = 0; j < i; j++) free(str_arr[j]);
+            free(str_arr);
+            return ENOMEM;
+        }
+    }
+
+    error = ini_config_add_str_arr_value(ini_config,
+                                         section,
+                                         key,
+                                         (const char **)str_arr,
+                                         count_long,
+                                         sep,
+                                         comments,
+                                         count_comment,
+                                         border,
+                                         position,
+                                         other_key,
+                                         idx,
+                                         flags);
+
+    for (i = 0; i < count_long; i++) free(str_arr[i]);
+    free(str_arr);
+
+    TRACE_FLOW_RETURN(error);
+    return error;
+}
+
+/* Add or modify a value that stores array of doubles */
+int ini_config_add_double_arr_value(struct ini_cfgobj *ini_config,
+                                    const char *section,
+                                    const char *key,
+                                    double *value_double_arr,
+                                    size_t count_double,
+                                    char sep,
+                                    const char *comments[],
+                                    size_t count_comment,
+                                    int border,
+                                    int position,
+                                    const char *other_key,
+                                    int idx,
+                                    enum INI_VA flags)
+{
+    int error = EOK;
+    int ret = 0;
+    char **str_arr = NULL;
+    size_t i = 0;
+    size_t j = 0;
+
+    TRACE_FLOW_ENTRY();
+
+    if (!count_double) {
+        TRACE_ERROR_STRING("Invalid argument","count_double");
+        return EINVAL;
+    }
+
+    str_arr = malloc(count_double * sizeof(char *));
+    if (!str_arr) {
+        TRACE_ERROR_STRING("Failed to allocate memory for string array",
+                           ENOMEM);
+        return ENOMEM;
+    }
+
+    for (i = 0; i < count_double; i++) {
+        ret = asprintf(&str_arr[i], "%f", value_double_arr[i]);
+        if (ret == -1) {
+            TRACE_ERROR_NUMBER("Asprintf failed.", ret);
+            /* The main reason is propbaly memory allocation */
+            for (j = 0; j < i; j++) free(str_arr[j]);
+            free(str_arr);
+            return ENOMEM;
+        }
+    }
+
+    error = ini_config_add_str_arr_value(ini_config,
+                                         section,
+                                         key,
+                                         (const char **)str_arr,
+                                         count_double,
+                                         sep,
+                                         comments,
+                                         count_comment,
+                                         border,
+                                         position,
+                                         other_key,
+                                         idx,
+                                         flags);
+
+    for (i = 0; i < count_double; i++) free(str_arr[i]);
+    free(str_arr);
+
+    TRACE_FLOW_RETURN(error);
+    return error;
+}
+
+/* Add or modify a value that stores array of strings */
+int ini_config_add_str_arr_value(struct ini_cfgobj *ini_config,
+                                 const char *section,
+                                 const char *key,
+                                 const char *value_str_arr[],
+                                 size_t count_str,
+                                 char sep,
+                                 const char *comments[],
+                                 size_t count_comment,
+                                 int border,
+                                 int position,
+                                 const char *other_key,
+                                 int idx,
+                                 enum INI_VA flags)
+{
+    int error = EOK;
+    size_t len = 0;
+    size_t i = 0;
+    struct simplebuffer *sbobj = NULL;
+    char sp[3] = "  ";
+
+    TRACE_FLOW_ENTRY();
+
+    if (!count_str) {
+        TRACE_ERROR_STRING("Invalid argument","count_str");
+        return EINVAL;
+    }
+
+    error = simplebuffer_alloc(&sbobj);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to allocate dynamic string.", error);
+        return error;
+    }
+
+    sp[0] = sep;
+
+    for (i = 0; i < count_str - 1; i++) {
+        len = strlen(value_str_arr[i]);
+        if ((error = simplebuffer_add_str(sbobj,
+                                          value_str_arr[i],
+                                          len,
+                                          INI_VALUE_BLOCK)) ||
+            (error = simplebuffer_add_str(sbobj,
+                                          sp,
+                                          2,
+                                          INI_VALUE_BLOCK))) {
+            TRACE_ERROR_NUMBER("String append failed.", error);
+            simplebuffer_free(sbobj);
+            return error;
+        }
+    }
+    len = strlen(value_str_arr[count_str - 1]);
+    error = simplebuffer_add_str(sbobj,
+                                 value_str_arr[count_str - 1],
+                                 len,
+                                 INI_VALUE_BLOCK);
+    if (error) {
+        TRACE_ERROR_NUMBER("String append failed.", error);
+        simplebuffer_free(sbobj);
+        return error;
+    }
+
+    error = ini_config_add_str_value(ini_config,
+                                     section,
+                                     key,
+                                     (const char *)simplebuffer_get_buf(sbobj),
+                                     comments,
+                                     count_comment,
+                                     border,
+                                     position,
+                                     other_key,
+                                     idx,
+                                     flags);
+
+    simplebuffer_free(sbobj);
+
+    TRACE_FLOW_RETURN(error);
+    return error;
+}
+
+/* Function to add integer value */
+int ini_config_add_int_value(struct ini_cfgobj *ini_config,
+                             const char *section,
+                             const char *key,
+                             int value,
+                             const char **comments,
+                             size_t count_comment,
+                             int border,
+                             int position,
+                             const char *other_key,
+                             int idx,
+                             enum INI_VA flags)
+{
+    int error = EOK;
+    int ret = 0;
+    char *strval = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    ret = asprintf(&strval, "%d", value);
+    if (ret == -1) {
+        TRACE_ERROR_NUMBER("Asprintf failed.", ret);
+        /* The main reason is propbaly memory allocation */
+        return ENOMEM;
+    }
+
+    /* Call string function */
+    error = ini_config_add_str_value(ini_config,
+                                     section,
+                                     key,
+                                     strval,
+                                     comments,
+                                     count_comment,
+                                     border,
+                                     position,
+                                     other_key,
+                                     idx,
+                                     flags);
+    TRACE_FLOW_RETURN(error);
+    free(strval);
+    return error;
+}
+
+
+/* Function to add long value */
+int ini_config_add_long_value(struct ini_cfgobj *ini_config,
+                              const char *section,
+                              const char *key,
+                              long value,
+                              const char **comments,
+                              size_t count_comment,
+                              int border,
+                              int position,
+                              const char *other_key,
+                              int idx,
+                              enum INI_VA flags)
+{
+    int error = EOK;
+    int ret = 0;
+    char *strval = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    ret = asprintf(&strval, "%ld", value);
+    if (ret == -1) {
+        TRACE_ERROR_NUMBER("Asprintf failed.", ret);
+        /* The main reason is propbaly memory allocation */
+        return ENOMEM;
+    }
+
+    /* Call string function */
+    error = ini_config_add_str_value(ini_config,
+                                     section,
+                                     key,
+                                     strval,
+                                     comments,
+                                     count_comment,
+                                     border,
+                                     position,
+                                     other_key,
+                                     idx,
+                                     flags);
+    TRACE_FLOW_RETURN(error);
+    free(strval);
+    return error;
+}
+
+
+/* Function to add ulong value */
+int ini_config_add_ulong_value(struct ini_cfgobj *ini_config,
+                               const char *section,
+                               const char *key,
+                               unsigned long value,
+                               const char **comments,
+                               size_t count_comment,
+                               int border,
+                               int position,
+                               const char *other_key,
+                               int idx,
+                               enum INI_VA flags)
+{
+    int error = EOK;
+    int ret = 0;
+    char *strval = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    ret = asprintf(&strval, "%lu", value);
+    if (ret == -1) {
+        TRACE_ERROR_NUMBER("Asprintf failed.", ret);
+        /* The main reason is propbaly memory allocation */
+        return ENOMEM;
+    }
+
+    /* Call string function */
+    error = ini_config_add_str_value(ini_config,
+                                     section,
+                                     key,
+                                     strval,
+                                     comments,
+                                     count_comment,
+                                     border,
+                                     position,
+                                     other_key,
+                                     idx,
+                                     flags);
+    TRACE_FLOW_RETURN(error);
+    free(strval);
+    return error;
+}
+
+
+/* Function to add unsigned value */
+int ini_config_add_unsigned_value(struct ini_cfgobj *ini_config,
+                                  const char *section,
+                                  const char *key,
+                                  unsigned value,
+                                  const char **comments,
+                                  size_t count_comment,
+                                  int border,
+                                  int position,
+                                  const char *other_key,
+                                  int idx,
+                                  enum INI_VA flags)
+{
+    int error = EOK;
+    int ret = 0;
+    char *strval = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    ret = asprintf(&strval, "%u", value);
+    if (ret == -1) {
+        TRACE_ERROR_NUMBER("Asprintf failed.", ret);
+        /* The main reason is propbaly memory allocation */
+        return ENOMEM;
+    }
+
+    /* Call string function */
+    error = ini_config_add_str_value(ini_config,
+                                     section,
+                                     key,
+                                     strval,
+                                     comments,
+                                     count_comment,
+                                     border,
+                                     position,
+                                     other_key,
+                                     idx,
+                                     flags);
+    TRACE_FLOW_RETURN(error);
+    free(strval);
+    return error;
+}
+
+/* Function to add int32 value */
+int ini_config_add_int32_value(struct ini_cfgobj *ini_config,
+                               const char *section,
+                               const char *key,
+                               int32_t value,
+                               const char **comments,
+                               size_t count_comment,
+                               int border,
+                               int position,
+                               const char *other_key,
+                               int idx,
+                               enum INI_VA flags)
+{
+    int error = EOK;
+    int ret = 0;
+    char *strval = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    ret = asprintf(&strval, "%d", value);
+    if (ret == -1) {
+        TRACE_ERROR_NUMBER("Asprintf failed.", ret);
+        /* The main reason is propbaly memory allocation */
+        return ENOMEM;
+    }
+
+    /* Call string function */
+    error = ini_config_add_str_value(ini_config,
+                                     section,
+                                     key,
+                                     strval,
+                                     comments,
+                                     count_comment,
+                                     border,
+                                     position,
+                                     other_key,
+                                     idx,
+                                     flags);
+    TRACE_FLOW_RETURN(error);
+    free(strval);
+    return error;
+}
+
+/* Function to add uint32 value */
+int ini_config_add_uint32_value(struct ini_cfgobj *ini_config,
+                                const char *section,
+                                const char *key,
+                                uint32_t value,
+                                const char **comments,
+                                size_t count_comment,
+                                int border,
+                                int position,
+                                const char *other_key,
+                                int idx,
+                                enum INI_VA flags)
+{
+    int error = EOK;
+    int ret = 0;
+    char *strval = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    ret = asprintf(&strval, "%u", value);
+    if (ret == -1) {
+        TRACE_ERROR_NUMBER("Asprintf failed.", ret);
+        /* The main reason is propbaly memory allocation */
+        return ENOMEM;
+    }
+
+    /* Call string function */
+    error = ini_config_add_str_value(ini_config,
+                                     section,
+                                     key,
+                                     strval,
+                                     comments,
+                                     count_comment,
+                                     border,
+                                     position,
+                                     other_key,
+                                     idx,
+                                     flags);
+    TRACE_FLOW_RETURN(error);
+    free(strval);
+    return error;
+}
+
+/* Function to add int64 value */
+int ini_config_add_int64_value(struct ini_cfgobj *ini_config,
+                               const char *section,
+                               const char *key,
+                               int64_t value,
+                               const char **comments,
+                               size_t count_comment,
+                               int border,
+                               int position,
+                               const char *other_key,
+                               int idx,
+                               enum INI_VA flags)
+{
+    int error = EOK;
+    int ret = 0;
+    char *strval = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    ret = asprintf(&strval, "%lld", (long long)value);
+    if (ret == -1) {
+        TRACE_ERROR_NUMBER("Asprintf failed.", ret);
+        /* The main reason is propbaly memory allocation */
+        return ENOMEM;
+    }
+
+    /* Call string function */
+    error = ini_config_add_str_value(ini_config,
+                                     section,
+                                     key,
+                                     strval,
+                                     comments,
+                                     count_comment,
+                                     border,
+                                     position,
+                                     other_key,
+                                     idx,
+                                     flags);
+    TRACE_FLOW_RETURN(error);
+    free(strval);
+    return error;
+}
+
+/* Function to add uint64 value */
+int ini_config_add_uint64_value(struct ini_cfgobj *ini_config,
+                                  const char *section,
+                                  const char *key,
+                                  uint64_t value,
+                                  const char **comments,
+                                  size_t count_comment,
+                                  int border,
+                                  int position,
+                                  const char *other_key,
+                                  int idx,
+                                  enum INI_VA flags)
+{
+    int error = EOK;
+    int ret = 0;
+    char *strval = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    ret = asprintf(&strval, "%llu", (unsigned long long)value);
+    if (ret == -1) {
+        TRACE_ERROR_NUMBER("Asprintf failed.", ret);
+        /* The main reason is propbaly memory allocation */
+        return ENOMEM;
+    }
+
+    /* Call string function */
+    error = ini_config_add_str_value(ini_config,
+                                     section,
+                                     key,
+                                     strval,
+                                     comments,
+                                     count_comment,
+                                     border,
+                                     position,
+                                     other_key,
+                                     idx,
+                                     flags);
+    TRACE_FLOW_RETURN(error);
+    free(strval);
+    return error;
+}
+
+
+/* Function to add double value */
+int ini_config_add_double_value(struct ini_cfgobj *ini_config,
+                                const char *section,
+                                const char *key,
+                                double value,
+                                const char **comments,
+                                size_t count_comment,
+                                int border,
+                                int position,
+                                const char *other_key,
+                                int idx,
+                                enum INI_VA flags)
+{
+    int error = EOK;
+    int ret = 0;
+    char *strval = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    ret = asprintf(&strval, "%f", value);
+    if (ret == 1) {
+        TRACE_ERROR_NUMBER("Asprintf failed.", ret);
+        /* The main reason is propbaly memory allocation */
+        return ENOMEM;
+    }
+
+    /* Call string function */
+    error = ini_config_add_str_value(ini_config,
+                                     section,
+                                     key,
+                                     strval,
+                                     comments,
+                                     count_comment,
+                                     border,
+                                     position,
+                                     other_key,
+                                     idx,
+                                     flags);
+    TRACE_FLOW_RETURN(error);
+    free(strval);
+    return error;
+}
+
+/* Function to add binary value */
+int ini_config_add_bin_value(struct ini_cfgobj *ini_config,
+                             const char *section,
+                             const char *key,
+                             void *value,
+                             size_t value_len,
+                             const char **comments,
+                             size_t count_comment,
+                             int border,
+                             int position,
+                             const char *other_key,
+                             int idx,
+                             enum INI_VA flags)
+{
+    int error = EOK;
+    size_t i;
+    char *strval = NULL;
+    char *ptr = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    /* Do we have the vo ? */
+    if ((!value) && (value_len)) {
+        TRACE_ERROR_NUMBER("Invalid argument.", EINVAL);
+        return EINVAL;
+    }
+
+    /* The value is good so we can allocate memory for it */
+    strval = malloc(value_len * 2 + 3);
+    if (strval == NULL) {
+        TRACE_ERROR_NUMBER("Failed to allocate memory.", ENOMEM);
+        return ENOMEM;
+    }
+
+    strval[0] = '\'';
+
+    /* Convert the value */
+    ptr = strval + 1;
+    for (i = 0; i < value_len; i++) {
+        sprintf(ptr, "%02x", *((unsigned char *)(value) + i));
+        ptr += 2;
+    }
+
+    strval[value_len * 2 + 1] = '\'';
+    strval[value_len * 2 + 2] = '\0';
+
+    /* Call string function */
+    error = ini_config_add_str_value(ini_config,
+                                     section,
+                                     key,
+                                     strval,
+                                     comments,
+                                     count_comment,
+                                     border,
+                                     position,
+                                     other_key,
+                                     idx,
+                                     flags);
+    free(strval);
+    TRACE_FLOW_RETURN(error);
+    return error;
+}
+
+
+/* Function to add string value */
+int ini_config_add_str_value(struct ini_cfgobj *ini_config,
+                             const char *section,
+                             const char *key,
+                             const char *value,
+                             const char **comments,
+                             size_t count_comment,
+                             int border,
+                             int position,
+                             const char *other_key,
+                             int idx,
+                             enum INI_VA flags)
+{
+    int error = EOK;
+    struct ini_comment *ic = NULL;
+    struct value_obj *vo = NULL;
+    struct value_obj *old_vo = NULL;
+    struct collection_item *item = NULL;
+    const char sp_key[] = INI_SECTION_KEY;
+    const char *key_ptr;
+
+    TRACE_FLOW_ENTRY();
+
+    /* Check arguments */
+    if (!ini_config) {
+        TRACE_ERROR_STRING("Invalid argument","ini_config");
+        return EINVAL;
+    }
+
+    if (!section) {
+        TRACE_ERROR_STRING("Invalid argument","section");
+        return EINVAL;
+    }
+
+    if (!key) {
+        TRACE_ERROR_STRING("Invalid argument","key");
+        return EINVAL;
+    }
+
+    if (!value) {
+        TRACE_ERROR_STRING("Invalid argument","value");
+        return EINVAL;
+    }
+
+    if (idx < 0) {
+        TRACE_ERROR_STRING("Invalid argument","idx");
+        return EINVAL;
+    }
+
+
+    switch (flags) {
+
+    case INI_VA_NOCHECK:    /* Just fall through */
+                            break;
+
+    case INI_VA_MOD:
+    case INI_VA_MOD_E:
+                            /* Find the value by index.
+                             * If value is not found return error.
+                             */
+                            error = col_get_dup_item(ini_config->cfg,
+                                                     section,
+                                                     key,
+                                                     COL_TYPE_ANY,
+                                                     idx,
+                                                     EXACT(flags),
+                                                     &item);
+                            if (error) {
+                                TRACE_ERROR_NUMBER("Error "
+                                                   "looking for item.",
+                                                   error);
+                                return error;
+                            }
+                            break;
+
+
+    case INI_VA_MODADD:
+    case INI_VA_MODADD_E:
+                            /* Find the value by index.
+                             * If value is not found it is OK.
+                             */
+                            error = col_get_dup_item(ini_config->cfg,
+                                                     section,
+                                                     key,
+                                                     COL_TYPE_ANY,
+                                                     idx,
+                                                     EXACT(flags),
+                                                     &item);
+                            if ((error) && (error != ENOENT)) {
+                                TRACE_ERROR_NUMBER("Unexpected error "
+                                                   "looking for item.",
+                                                   error);
+                                return error;
+                            }
+                            break;
+
+
+
+    case INI_VA_DUPERROR:   /* Find any instance */
+                            error = col_get_dup_item(ini_config->cfg,
+                                                     section,
+                                                     key,
+                                                     COL_TYPE_ANY,
+                                                     0,
+                                                     0,
+                                                     &item);
+                            if ((error) && (error != ENOENT)) {
+                                TRACE_ERROR_NUMBER("Unexpected error "
+                                                   "looking for item.",
+                                                   error);
+                                return error;
+                            }
+                            if (!error) {
+                                TRACE_ERROR_NUMBER("Key exists "
+                                                   "this is error.",
+                                                   error);
+                                return EEXIST;
+                            }
+                            break;
+    case INI_VA_CLEAN:      /*  Delete all instaces of the key first */
+                            while (!error) {
+                                error = col_remove_item(ini_config->cfg,
+                                                        section,
+                                                        COL_DSP_FIRSTDUP,
+                                                        key,
+                                                        0,
+                                                        COL_TYPE_ANY);
+                                if (error) {
+                                    if (error != ENOENT) {
+                                        TRACE_ERROR_NUMBER("Failed to clean "
+                                                           "the section.",
+                                                           error);
+                                        return error;
+                                    }
+                                    else break;
+                                }
+                            }
+                            break;
+    default:                /* The new ones should be added here */
+                            TRACE_ERROR_NUMBER("Flag is not implemented",
+                                               ENOSYS);
+                            return ENOSYS;
+    }
+
+    /* Start with the comment */
+    if (comments) {
+        error = ini_comment_construct(comments,
+                                      count_comment,
+                                      &ic);
+        if (error) {
+            TRACE_ERROR_NUMBER("Failed to construct comment", error);
+            return error;
+        }
+    }
+
+    /* Create value object */
+    error =  value_create_new(value,
+                              strnlen(value, MAX_VALUE -1),
+                              INI_VALUE_CREATED,
+                              strnlen(key, MAX_KEY -1),
+                              border,
+                              ic,
+                              &vo);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to construct value object.", error);
+        ini_comment_destroy(ic);
+        return error;
+    }
+
+    if (item) {
+        /* If we have selected item remove old value */
+        old_vo = *((struct value_obj **)(col_get_item_data(item)));
+        value_destroy(old_vo);
+        /* Update the item with the new value */
+        error =  col_modify_binary_item(item,
+                                        NULL,
+                                        &vo,
+                                        sizeof(struct value_obj *));
+        if (error) {
+            TRACE_ERROR_NUMBER("Failed to update item.", error);
+            value_destroy(vo);
+            return error;
+        }
+    }
+    else {
+        if (position == COL_DSP_FRONT) {
+            key_ptr = sp_key;
+            position = COL_DSP_AFTER;
+        }
+        else {
+            key_ptr = other_key;
+        }
+        /* Add value to collection */
+        error = col_insert_binary_property(ini_config->cfg,
+                                           section,
+                                           position,
+                                           key_ptr,
+                                           idx,
+                                           flags,
+                                           key,
+                                           &vo,
+                                           sizeof(struct value_obj *));
+    }
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to insert value.", error);
+        value_destroy(vo);
+        return error;
+    }
+
+    TRACE_FLOW_EXIT();
+    return EOK;
+}
+
+/* Function to add section */
+int ini_config_add_section(struct ini_cfgobj *ini_config,
+                           const char *section,
+                           const char *comments[],
+                           size_t count_comment,
+                           int position,
+                           const char *other_section,
+                           int idx)
+{
+    int error = EOK;
+    struct ini_comment *ic = NULL;
+    struct value_obj *vo = NULL;
+    struct collection_item *item = NULL;
+    struct collection_item *sec = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    /* Check arguments */
+    if (!ini_config) {
+        TRACE_ERROR_STRING("Invalid argument","ini_config");
+        return EINVAL;
+    }
+
+    if (!section) {
+        TRACE_ERROR_STRING("Invalid argument","section");
+        return EINVAL;
+    }
+
+    if (position > COL_DSP_INDEX) {
+        TRACE_ERROR_STRING("Invalid argument","position");
+        return EINVAL;
+    }
+
+    if (idx < 0) {
+        TRACE_ERROR_STRING("Invalid argument","idx");
+        return EINVAL;
+    }
+
+    /* Check if section exists */
+    error = col_get_item(ini_config->cfg,
+                         section,
+                         COL_TYPE_COLLECTIONREF,
+                         COL_TRAVERSE_ONELEVEL,
+                         &item);
+    if (error) {
+        TRACE_ERROR_NUMBER("Search for section failed.", error);
+        return error;
+    }
+
+    if (item) {
+        TRACE_ERROR_STRING("Section already exists.", section);
+        return EEXIST;
+    }
+
+    /* Create a new section */
+    error = col_create_collection(&sec,
+                                  section,
+                                  COL_CLASS_INI_SECTION);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to create a section", error);
+        return error;
+    }
+
+    /* Process comment */
+    if (comments) {
+        error = ini_comment_construct(comments,
+                                      count_comment,
+                                      &ic);
+        if (error) {
+            TRACE_ERROR_NUMBER("Failed to construct comment", error);
+            col_destroy_collection(sec);
+            return error;
+        }
+    }
+
+    /* Create value object */
+    error =  value_create_new(section,
+                              strnlen(section, MAX_VALUE -1),
+                              INI_VALUE_CREATED,
+                              strlen(INI_SECTION_KEY),
+                              INI_WRAP_BOUNDARY,
+                              ic,
+                              &vo);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to construct value object.", error);
+        ini_comment_destroy(ic);
+        col_destroy_collection(sec);
+        return error;
+    }
+    /* Comment is now a part of value - no need to clean it seprately. */
+
+    /* Add value to section collection */
+    error = col_insert_binary_property(sec,
+                                       NULL,
+                                       COL_DSP_END,
+                                       NULL,
+                                       0,
+                                       COL_INSERT_NOCHECK,
+                                       INI_SECTION_KEY,
+                                       &vo,
+                                       sizeof(struct value_obj *));
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to add value object to section.", error);
+        value_destroy(vo);
+        col_destroy_collection(sec);
+        return error;
+    }
+
+    /* Embed section collection */
+    /* Since there is no function to do addition of collection to
+     * collection with disposition we will use a workaround.
+     */
+
+    error = col_insert_property_with_ref(ini_config->cfg,
+                                         NULL,
+                                         position,
+                                         other_section,
+                                         idx,
+                                         COL_INSERT_NOCHECK,
+                                         section,
+                                         COL_TYPE_COLLECTIONREF,
+                                         (void *)(&sec),
+                                         sizeof(struct collection_item **),
+                                         NULL);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to embed section", error);
+        value_destroy(vo);
+        col_destroy_collection(sec);
+        return error;
+    }
+
+    TRACE_FLOW_EXIT();
+    return EOK;
+}
+
+/* Function to add or remove comments for a section */
+int ini_config_comment_section(struct ini_cfgobj *ini_config,
+                               const char *section,
+                               const char *comments[],
+                               size_t count_comment)
+{
+    int error = EOK;
+    struct ini_comment *ic = NULL;
+    struct value_obj *vo = NULL;
+    struct collection_item *item = NULL;
+    struct collection_item *sec = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    /* Check arguments */
+    if (!ini_config) {
+        TRACE_ERROR_STRING("Invalid argument","ini_config");
+        return EINVAL;
+    }
+
+    if (!section) {
+        TRACE_ERROR_STRING("Invalid argument","section");
+        return EINVAL;
+    }
+
+    /* Get the section */
+    error = col_get_item(ini_config->cfg,
+                         section,
+                         COL_TYPE_COLLECTIONREF,
+                         COL_TRAVERSE_ONELEVEL,
+                         &item);
+    if (error) {
+        TRACE_ERROR_NUMBER("Search for section failed.", error);
+        return error;
+    }
+
+    /* If item not found return error */
+    if (!item) {
+        TRACE_ERROR_NUMBER("Item not found.", ENOENT);
+        return ENOENT;
+    }
+
+    /* Item is actually a section reference */
+    sec = *((struct collection_item **)col_get_item_data(item));
+    item = NULL;
+
+    /* Now get the special item from the section collection */
+    error = col_get_item(sec,
+                         INI_SECTION_KEY,
+                         COL_TYPE_ANY,
+                         COL_TRAVERSE_ONELEVEL,
+                         &item);
+    if (error) {
+        TRACE_ERROR_NUMBER("Search for section failed.", error);
+        return error;
+    }
+
+    /* If item not found return error */
+    if (!item) {
+        /* Something is really broken with the internal implementation
+         * if we can't find the item, thus EINVAL.
+         */
+        TRACE_ERROR_NUMBER("Item not found.", EINVAL);
+        return EINVAL;
+    }
+
+    /* Item is actually a value object. */
+    vo = *((struct value_obj **)(col_get_item_data(item)));
+
+    /* Build comment */
+    if (comments) {
+        error = ini_comment_construct(comments,
+                                      count_comment,
+                                      &ic);
+        if (error) {
+            TRACE_ERROR_NUMBER("Failed to construct comment", error);
+            return error;
+        }
+    }
+
+    /* Replace comment with the new one. Old one is freed by the function */
+    error = value_put_comment(vo, ic);
+    if (error) {
+        TRACE_ERROR_NUMBER("Faile to update comment.",
+                            error);
+        ini_comment_destroy(ic);
+        return error;
+    }
+
+    TRACE_FLOW_EXIT();
+    return EOK;
+}
+
+
+/* Function to rename section */
+int ini_config_rename_section(struct ini_cfgobj *ini_config,
+                              const char *section,
+                              const char *newname)
+{
+    int error = EOK;
+    struct collection_item *item = NULL;
+    struct collection_item *sec = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    /* Check arguments */
+    if (!ini_config) {
+        TRACE_ERROR_STRING("Invalid argument","ini_config");
+        return EINVAL;
+    }
+
+    if (!section) {
+        TRACE_ERROR_STRING("Invalid argument","section");
+        return EINVAL;
+    }
+
+    if (!newname) {
+        TRACE_ERROR_STRING("Invalid argument","newname");
+        return EINVAL;
+    }
+
+    /* Get the section */
+    error = col_get_item(ini_config->cfg,
+                         section,
+                         COL_TYPE_COLLECTIONREF,
+                         COL_TRAVERSE_ONELEVEL,
+                         &item);
+    if (error) {
+        TRACE_ERROR_NUMBER("Search for section failed.", error);
+        return error;
+    }
+
+    /* If item not found return error */
+    if (!item) {
+        TRACE_ERROR_NUMBER("Item not found.", ENOENT);
+        return ENOENT;
+    }
+
+    /* Item is actually a section reference */
+    sec = *((struct collection_item **)col_get_item_data(item));
+
+    /* Change name only */
+    error = col_modify_item(item,
+                            newname,
+                            0,
+                            NULL,
+                            0);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to change section name.", error);
+        return error;
+    }
+
+    /* Change name of the embedded collection (for consistency) */
+    error = col_modify_item(sec,
+                            newname,
+                            0,
+                            NULL,
+                            0);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to change section name "
+                           "inside the embedded collection.", error);
+        return error;
+    }
+
+    TRACE_FLOW_EXIT();
+    return EOK;
+}
+
+/* Cleanup collback */
+static void cb(const char *property,
+               int property_len,
+               int type,
+               void *data,
+               int length,
+               void *ext_data)
+{
+    struct value_obj *vo;
+    TRACE_FLOW_ENTRY();
+
+    if ((type == COL_TYPE_COLLECTIONREF) ||
+        (type == COL_TYPE_COLLECTION)) return;
+
+    vo = *((struct value_obj **)(data));
+    value_destroy(vo);
+
+    TRACE_FLOW_EXIT();
+
+}
+
+/* Function to delete section by name */
+int ini_config_delete_section_by_name(struct ini_cfgobj *ini_config,
+                                      const char *section)
+{
+    int error = EOK;
+
+    TRACE_FLOW_ENTRY();
+    error = ini_config_delete_section_by_position(ini_config,
+                                                  COL_DSP_FIRSTDUP,
+                                                  section,
+                                                  0);
+    if (error) {
+        TRACE_ERROR_NUMBER("Search for section failed.", error);
+        return error;
+    }
+
+    TRACE_FLOW_EXIT();
+    return EOK;
+}
+
+/* Function to delete section by relative postion */
+int ini_config_delete_section_by_position(struct ini_cfgobj *ini_config,
+                                          int position,
+                                          const char *section,
+                                          int idx)
+{
+    int error = EOK;
+    struct collection_item *item = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    /* Check arguments */
+    if (!ini_config) {
+        TRACE_ERROR_STRING("Invalid argument","ini_config");
+        return EINVAL;
+    }
+
+    if (!section) {
+        TRACE_ERROR_STRING("Invalid argument","section");
+        return EINVAL;
+    }
+
+    if (idx < 0) {
+        TRACE_ERROR_STRING("Invalid argument","idx");
+        return EINVAL;
+    }
+
+    /* Extract section */
+    error = col_extract_item(ini_config->cfg,
+                             NULL,
+                             position,
+                             section,
+                             idx,
+                             COL_TYPE_ANY,
+                             &item);
+    if (error) {
+        TRACE_ERROR_NUMBER("Search for section failed.", error);
+        return error;
+    }
+
+    /* If item not found return error */
+    if (!item) {
+        TRACE_ERROR_NUMBER("Item not found.", ENOENT);
+        return ENOENT;
+    }
+
+    /* Delete item and subcollection */
+    col_delete_item_with_cb(item, cb, NULL);
+
+    TRACE_FLOW_EXIT();
+    return EOK;
+}
diff --git a/ini/ini_configmod.h b/ini/ini_configmod.h
new file mode 100644
index 0000000000000000000000000000000000000000..e25a2756fab9121e64829aa844e28062cc78df23
--- /dev/null
+++ b/ini/ini_configmod.h
@@ -0,0 +1,744 @@
+/*
+    INI LIBRARY
+
+    Interface that allows modification of the INI file.
+
+    Copyright (C) Dmitri Pal <dpal@redhat.com> 2014
+
+    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_CONFIGMOD_H
+#define INI_CONFIGMOD_H
+
+
+/**
+ * @defgroup insflags Flags used in add_xxx_value functions
+ *
+ * These flags control the behavior of the add_xxx_value functions.
+ * Use these flags to specify how you want to process the key.
+ * Do you want the function to replace an existing key? Or may be you want to
+ * generate an error when function sees that there is a duplicate key?
+ * Or may be you do not want to check anything and just add the key
+ * to the section?
+ *
+ * Individual values provide more details about use.
+ *
+ * @{
+ */
+enum INI_VA {
+/** @brief No check
+ *
+ * This is the default mode. Function will not perform
+ * any checks for duplicates and will just add a key.
+ */
+    INI_VA_NOCHECK    = 0,
+/**
+ * @brief Update a specific value (best effort).
+ *
+ * Value of the index is used to determine which one of the duplicates
+ * needs to be updated. Index is 0-based. If the index is out of range
+ * the function will do best effort and return the last instance of the key.
+ * For example if you have five duplicates and you are searching for the tenth
+ * the function will find and return the fifth instance.
+ */
+    INI_VA_MOD        = 1,
+/**
+ * @brief Update a specific value (with error).
+ *
+ * Value of the index is used to determine which one of the duplicates
+ * needs to be updated. Index is 0-based. If the value is not found or
+ * the index is out of range the function returns ENOENT.
+ * For example if you have five duplicates and you are searching for the tenth
+ * the error ENOENT will be returned.
+ */
+    INI_VA_MOD_E      = 2,
+
+/**
+ * @brief Update or add if not found (best effort).
+ *
+ * Function will check for duplicates and would overwrite the specified
+ * key if found.
+ * Value of the index is used to determine which one of the duplicates
+ * needs to be updated. Index is 0-based. If the value is not found
+ * the new value will be added following other position related arguments.
+ * This option performs best effort search.
+ * For example if you have five duplicates and you are searching for the tenth
+ * the function will find and return fifth instance.
+ */
+    INI_VA_MODADD     = 3,
+
+/**
+ * @brief Update or add if not found (with error).
+ *
+ * Function will check for duplicates and would overwrite the specified
+ * key if found.
+ * Value of the index is used to determine which one of the duplicates
+ * needs to be updated. Index is 0-based. If the value is not found
+ * the new value will be added following other position related arguments.
+ * This option performs an exact search.
+ * For example if you have five duplicates and you are searching for tenth
+ * the error ENOENT will be returned.
+ */
+    INI_VA_MODADD_E   = 4,
+
+/** @brief Clean all duplicates.
+ *
+ * Find all instances of the specific key and remove them first.
+ * Then add the new key respecting the position related arguments.
+ */
+    INI_VA_CLEAN      = 5,
+
+/** @brief Error if duplicate
+ *
+ * Return error EEXIST if an entry with the same key exists.
+ */
+    INI_VA_DUPERROR   = 6
+};
+/**
+ * @}
+ */
+
+/**
+ * @defgroup ini_mod API to create and modify configuration objects
+ *
+ * Functions in this section allow creation, modification
+ * and deletion of sections and keys.
+ *
+ * Explanation of some of the common arguments:
+ *
+ * @param[in]  position        Specifies the position a key or a section
+ *                             should be added to.\n\n
+ *                             Options include:
+ *                             - Last in the list
+ *                             - First in the list
+ *                             - N-th element of the list
+ *                             - Before a specific element of the list
+ *                             - After a specific element of the list
+ *                             - As a first duplicate of a specific element
+ *                             - As a last duplicate of a specific element
+ *                             - As an N-th duplicate of a specific element
+ *                             \n\n
+ *                             Duplicates can be sequential or not, so there
+ *                             are different constants that specify whether
+ *                             to count all duplicates or just the ones that
+ *                             go together.\n
+ * @param[in]  other_xxx       Used in conjunction with 'position' constant.
+ *                             In some cases it is convenient to add
+ *                             a section or key immediately before
+ *                             or after another section or key.
+ *                             While 'position' defines
+ *                             where it should be added, this parameter defines
+ *                             the section or key to relate to.
+ * @param[in]  idx             Index allows treating set of sections or keys
+ *                             as an array and add Nth section or key.
+
+ * @{
+ *
+ */
+
+
+/**
+ * @brief Function to add a section.
+ *
+ * Function creates a new section in the specified position.
+ * The position is relative to other sections. Duplicate sections
+ * are not allowed. In case a duplicate section is detected function
+ * will return error EEXIST.
+ *
+ * @param[in]  ini_config      Configuration object to modify.
+ * @param[in]  section         Name of the section to add.
+ * @param[in]  comments        Array of comments. Can be NULL, if no comment is
+ *                             needed for the section. It is expected that
+ *                             comment lines start with the correct comment
+ *                             symbol. Function does not check this.
+ *                             Use empty string to express an empty line.
+ * @param[in]  count_comment   Count of comments in the array. If 0,
+ *                             the array is expected to be NULL terminated
+ *                             and function will traverse array until its end.
+ * @param[in]  position        A constant that specifies the way the
+ *                             section should be added. For more information
+ *                             see "disposition constants" section of
+ *                             the collection (collection.h) interface.
+ *                             The value is used only in the add operation
+ *                             so see the explanation in the "insert" paragraph.
+ *                             Since duplicate sections are not allowed
+ *                             any constants that request addition of
+*                              a duplicate will cause function
+ *                             to return an error.
+ * @param[in]  other_section   Used in conjunction with position constant.
+ *                             In some cases it is convenient to add
+ *                             a section that is immediately before
+ *                             or after another section. While position defines
+ *                             where it should be added, this parameter defines
+ *                             the section to relate to.
+ * @param[in]  idx             Index, allows treating set of sections as
+ *                             an array and add Nth section.
+ *
+ * @return 0                   - Success.
+ * @return EEXIST              - Section already exists.
+ * @return ENOSYS              - Unknown position value.
+ * @return ENOMEM              - No memory to add value.
+ * @return EINVAL              - Invalid argument.
+ */
+int ini_config_add_section(struct ini_cfgobj *ini_config,
+                           const char *section,
+                           const char *comments[],
+                           size_t count_comment,
+                           int position,
+                           const char *other_section,
+                           int idx);
+
+/**
+ * @brief Function to update a comment for a section.
+ *
+ * Function finds a section and updates or removes its comment.
+ *
+ * @param[in]  ini_config      Configuration object to modify.
+ * @param[in]  section         Name of the section to change the comment for.
+ * @param[in]  comments        Array of comments. Can be NULL if no comment is
+ *                             needed for the section. It is expected that
+ *                             comment lines start with the correct comment
+ *                             symbol. Function does not check this.
+ *                             Use empty string to express an empty line.
+ * @param[in]  count_comment   Count of comments in the array. If 0
+ *                             the array is expected to be NULL terminated
+ *                             and function will traverse array until its end.
+ *
+ * @return 0                   - Success.
+ * @return ENOENT              - Section is not found.
+ * @return ENOMEM              - No memory to add value.
+ * @return EINVAL              - Invalid argument.
+ */
+int ini_config_comment_section(struct ini_cfgobj *ini_config,
+                               const char *section,
+                               const char *comments[],
+                               size_t count_comment);
+
+/**
+ * @brief Function to rename a section.
+ *
+ * Function finds a section and renames it.
+ *
+ * @param[in]  ini_config      Configuration object to modify.
+ * @param[in]  section         Name of the section to find.
+ * @param[in]  newname         New section name.
+ *
+ * @return 0                   - Success.
+ * @return ENOENT              - Section is not found.
+ * @return ENOMEM              - No memory to add value.
+ * @return EINVAL              - Invalid argument.
+ */
+int ini_config_rename_section(struct ini_cfgobj *ini_config,
+                              const char *section,
+                              const char *newname);
+
+
+/**
+ * @brief Function to delete a section by name.
+ *
+ * Function is a convenience wrapper around
+ * \ref ini_config_delete_section_by_position function.
+ *
+ * @param[in]  ini_config      Configuration object to modify.
+ * @param[in]  section         Name of the section to delete.
+ *
+ * @return 0                   - Success.
+ * @return ENOENT              - Section is not found.
+ * @return ENOMEM              - No memory to add value.
+ * @return EINVAL              - Invalid argument.
+ */
+int ini_config_delete_section_by_name(struct ini_cfgobj *ini_config,
+                                      const char *section);
+
+/**
+ * @brief Function to delete a section by position or name.
+ *
+ * Function allows removing a specific section by its relative position
+ * in the list of sections or by its name.
+ *
+ * @param[in]  ini_config      Configuration object to modify.
+ * @param[in]  position        A constant that specifies the way the
+ *                             section should be deleted. For more information
+ *                             see "disposition constants" section of
+ *                             the collection interface. The value is used
+ *                             only in delete operation so see the explanation
+ *                             in the "delete" paragraph.
+ *                             There can't be duplicate sections. However
+ *                             function respects all of the listed constants.
+ *                             To find and delete a particular section
+ *                             one can use COL_DSP_FIRSTDUP constant.
+ *                             One should just expect that there is only
+ *                             one instance of every section if any.
+ * @param[in]  section         Used in conjunction with position constant.
+ *                             In some cases it is convenient to add
+ *                             a section immediately before
+ *                             or after another value. While position defines
+ *                             where it should be added, this parameter defines
+ *                             the section to relate to.
+ * @param[in]  idx             Index of the position.
+ *                             For example one can specify
+ *                             that the third section should be deleted.
+ *
+ * @return 0                   - Success.
+ * @return ENOENT              - Section is not found.
+ * @return ENOMEM              - No memory to add value.
+ * @return EINVAL              - Invalid argument.
+ */
+int ini_config_delete_section_by_position(struct ini_cfgobj *ini_config,
+                                          int position,
+                                          const char *section,
+                                          int idx);
+
+
+/**
+ * @brief Function to a add string value to a particular section.
+ *
+ * Depending on the flags function adds, or deletes and adds, or just modifies
+ * the configuration value stored in the configuration object.
+ *
+ * \bNOTE: all other ini_config_add_xxx_value functions are implemented
+ *         as wrappers around this function so they have the same
+ *         behavior and logic.
+ *
+ * @param[in]  ini_config      Configuration object to modify.
+ * @param[in]  section         Name of the section to modify.
+ * @param[in]  key             Key of the value to add or modify.
+ * @param[in]  value           String that represents the value.
+ * @param[in]  comments        Array of comments. Can be NULL if no comment is
+ *                             needed for the section. It is expected that
+ *                             comment lines start with the correct comment
+ *                             symbol. Function does not check this.
+ *                             Use empty string to express an empty line.
+ * @param[in]  count_comment   Count of comments in the array. If 0,
+ *                             the array is expected to be NULL terminated
+ *                             and function will traverse array until its end.
+ * @param[in]  border          The boundary the value should be wrapped at.
+ *                             The usual value is 80 characters.
+ * @param[in]  position        A constant that specifies the way the
+ *                             value should be added. For more information
+ *                             see "disposition constants" section of
+ *                             the collection interface. The value is used
+ *                             only in add operation so see the explanation
+ *                             in the "insert" paragraph.
+ * @param[in]  other_key       Used in conjunction with 'position' constant.
+ *                             In some cases it is convenient to add
+ *                             a value that should be placed immediately before
+ *                             or after another value. While position defines
+ *                             where it should be added, this parameter defines
+ *                             the key to relate to.
+ * @param[in]  idx             Index in the whole section or among
+ *                             duplicates. For example one can specify
+ *                             that the value should be added as a third
+ *                             duplicate. This is convenient if INI file allows
+ *                             duplicate keys.
+ * @param[in]  flags           These constants define the main behavior of
+ *                             the function. The function can thus be used
+ *                             to build configuration object from scratch
+ *                             (in this case no checks would be needed),
+ *                             to modify a specific key which can be identified
+ *                             by index among duplicate keys in the same
+ *                             section. For more information see \ref insflags.
+ *
+ * @return 0                   - Success.
+ * @return EEXIST              - The value already exists.
+ * @return ENOENT              - The value not found.
+ * @return ENOSYS              - Unknown flag or position value.
+ * @return ENOMEM              - No memory to add value.
+ * @return EINVAL              - Invalid argument.
+ */
+int ini_config_add_str_value(struct ini_cfgobj *ini_config,
+                             const char *section,
+                             const char *key,
+                             const char *value,
+                             const char *comments[],
+                             size_t count_comment,
+                             int border,
+                             int position,
+                             const char *other_key,
+                             int idx,
+                             enum INI_VA flags);
+
+/**
+ * @brief Function to add an integer value to a particular section.
+ *
+ * Function is similar to \ref ini_config_add_str_value except it uses
+ * an integer argument for the value input.
+ *
+ * For logic, behavior and return values see \ref ini_config_add_str_value.
+ */
+int ini_config_add_int_value(struct ini_cfgobj *ini_config,
+                             const char *section,
+                             const char *key,
+                             int value,
+                             const char *comments[],
+                             size_t count_comment,
+                             int border,
+                             int position,
+                             const char *other_key,
+                             int idx,
+                             enum INI_VA flags);
+
+/**
+ * @brief Function to add a long value to a particular section.
+ *
+ * Function is similar to \ref ini_config_add_str_value except it uses
+ * a long integer argument for the value input.
+ *
+ * For logic, behavior and return values see \ref ini_config_add_str_value.
+ */
+int ini_config_add_long_value(struct ini_cfgobj *ini_config,
+                              const char *section,
+                              const char *key,
+                              long value,
+                              const char *comments[],
+                              size_t count_comment,
+                              int border,
+                              int position,
+                              const char *other_key,
+                              int idx,
+                              enum INI_VA flags);
+
+/**
+ * @brief Function to add an unsigned long value to a particular section.
+ *
+ * Function is similar to \ref ini_config_add_str_value except it uses
+ * an unsigned long integer argument for the value input.
+ *
+ * For logic, behavior and return values see \ref ini_config_add_str_value.
+ */
+int ini_config_add_ulong_value(struct ini_cfgobj *ini_config,
+                               const char *section,
+                               const char *key,
+                               unsigned long value,
+                               const char *comments[],
+                               size_t count_comment,
+                               int border,
+                               int position,
+                               const char *other_key,
+                               int idx,
+                               enum INI_VA flags);
+
+/**
+ * @brief Function to add an unsigned value to a particular section.
+ *
+ * Function is similar to \ref ini_config_add_str_value except it uses
+ * an unsigned argument for the value input.
+ *
+ * For logic, behavior and return values see \ref ini_config_add_str_value.
+ */
+int ini_config_add_unsigned_value(struct ini_cfgobj *ini_config,
+                                  const char *section,
+                                  const char *key,
+                                  unsigned value,
+                                  const char *comments[],
+                                  size_t count_comment,
+                                  int border,
+                                  int position,
+                                  const char *other_key,
+                                  int idx,
+                                  enum INI_VA flags);
+
+/**
+ * @brief Function to add an int32 value to a particular section.
+ *
+ * Function is similar to \ref ini_config_add_str_value except it uses
+ * an int32 argument for the value input.
+ *
+ * For logic, behavior and return values see \ref ini_config_add_str_value.
+ */
+int ini_config_add_int32_value(struct ini_cfgobj *ini_config,
+                               const char *section,
+                               const char *key,
+                               int32_t value,
+                               const char *comments[],
+                               size_t count_comment,
+                               int border,
+                               int position,
+                               const char *other_key,
+                               int idx,
+                               enum INI_VA flags);
+
+/**
+ * @brief Function to add an uint32 value to a particular section.
+ *
+ * Function is similar to \ref ini_config_add_str_value except it uses
+ * an uint32 argument for the value input.
+ *
+ * For logic, behavior and return values see \ref ini_config_add_str_value.
+ */
+int ini_config_add_uint32_value(struct ini_cfgobj *ini_config,
+                                const char *section,
+                                const char *key,
+                                uint32_t value,
+                                const char *comments[],
+                                size_t count_comment,
+                                int border,
+                                int position,
+                                const char *other_key,
+                                int idx,
+                                enum INI_VA flags);
+
+/**
+ * @brief Function to add an int64 value to a particular section.
+ *
+ * Function is similar to \ref ini_config_add_str_value except it uses
+ * an int64 argument for the value input.
+ *
+ * For logic, behavior and return values see \ref ini_config_add_str_value.
+ */
+int ini_config_add_int64_value(struct ini_cfgobj *ini_config,
+                               const char *section,
+                               const char *key,
+                               int64_t value,
+                               const char *comments[],
+                               size_t count_comment,
+                               int border,
+                               int position,
+                               const char *other_key,
+                               int idx,
+                               enum INI_VA flags);
+
+/**
+ * @brief Function to add an uint64 value to a particular section.
+ *
+ * Function is similar to \ref ini_config_add_str_value except it uses
+ * an uint64 argument for the value input.
+ *
+ * For logic, behavior and return values see \ref ini_config_add_str_value.
+ */
+int ini_config_add_uint64_value(struct ini_cfgobj *ini_config,
+                                const char *section,
+                                const char *key,
+                                uint64_t value,
+                                const char *comments[],
+                                size_t count_comment,
+                                int border,
+                                int position,
+                                const char *other_key,
+                                int idx,
+                                enum INI_VA flags);
+
+/**
+ * @brief Function to add a double value to a particular section.
+ *
+ * Function is similar to \ref ini_config_add_str_value except it uses
+ * a double argument for the value input.
+ *
+ * For logic, behavior and return values see \ref ini_config_add_str_value.
+ */
+int ini_config_add_double_value(struct ini_cfgobj *ini_config,
+                                const char *section,
+                                const char *key,
+                                double value,
+                                const char *comments[],
+                                size_t count_comment,
+                                int border,
+                                int position,
+                                const char *other_key,
+                                int idx,
+                                enum INI_VA flags);
+
+/**
+ * @brief Function to add a binary value to a particular section.
+ *
+ * Binary values are represented by a HEX string where two HEX digits
+ * represent one byte of the source data. The value is surrounded
+ * by a pair of single quotes.
+ *
+ * Function is similar to \ref ini_config_add_str_value except it uses
+ * a void argument for the value input. The length of the data is specified by
+ * by the value_len argument.
+ *
+ * For logic, behavior and return values see \ref ini_config_add_str_value.
+ */
+int ini_config_add_bin_value(struct ini_cfgobj *ini_config,
+                             const char *section,
+                             const char *key,
+                             void *value,
+                             size_t value_len,
+                             const char *comments[],
+                             size_t count_comment,
+                             int border,
+                             int position,
+                             const char *other_key,
+                             int idx,
+                             enum INI_VA flags);
+
+/**
+ * @brief Function to add a value that consists of an array of strings
+ *        to a particular section.
+ *
+ * Function is similar to \ref ini_config_add_str_value except it uses
+ * a string array argument and a count for the value input.
+ *
+ * For logic, behavior and return values see \ref ini_config_add_str_value.
+ */
+int ini_config_add_str_arr_value(struct ini_cfgobj *ini_config,
+                                 const char *section,
+                                 const char *key,
+                                 const char *value_str_arr[],
+                                 size_t count_str,
+                                 char sep,
+                                 const char *comments[],
+                                 size_t count_comment,
+                                 int border,
+                                 int position,
+                                 const char *other_key,
+                                 int idx,
+                                 enum INI_VA flags);
+
+/**
+ * @brief Function to add a value that consists of an array of integers
+ *        to a particular section.
+ *
+ * Function is similar to \ref ini_config_add_str_value except it uses
+ * a integer array argument and a count for the value input.
+ *
+ * For logic, behavior and return values see \ref ini_config_add_str_value.
+ */
+int ini_config_add_int_arr_value(struct ini_cfgobj *ini_config,
+                                 const char *section,
+                                 const char *key,
+                                 int *value_int_arr,
+                                 size_t count_int,
+                                 char sep,
+                                 const char *comments[],
+                                 size_t count_comment,
+                                 int border,
+                                 int position,
+                                 const char *other_key,
+                                 int idx,
+                                 enum INI_VA flags);
+
+/**
+ * @brief Function to add a value that consists of an array of long integers
+ *        to a particular section.
+ *
+ * Function is similar to \ref ini_config_add_str_value except it uses
+ * a long integer array argument and a count for the value input.
+ *
+ * For logic, behavior and return values see \ref ini_config_add_str_value.
+ */
+int ini_config_add_long_arr_value(struct ini_cfgobj *ini_config,
+                                  const char *section,
+                                  const char *key,
+                                  long *value_long_arr,
+                                  size_t count_long,
+                                  char sep,
+                                  const char *comments[],
+                                  size_t count_comment,
+                                  int border,
+                                  int position,
+                                  const char *other_key,
+                                  int idx,
+                                  enum INI_VA flags);
+
+/**
+ * @brief Function to add a value that consists of an array of doubles
+ *        to a particular section.
+ *
+ * Function is similar to \ref ini_config_add_str_value except it uses
+ * a double array argument and a count for the value input.
+ *
+ * For logic, behavior and return values see \ref ini_config_add_str_value.
+ */
+int ini_config_add_double_arr_value(struct ini_cfgobj *ini_config,
+                                    const char *section,
+                                    const char *key,
+                                    double *value_double_arr,
+                                    size_t count_double,
+                                    char sep,
+                                    const char *comments[],
+                                    size_t count_comment,
+                                    int border,
+                                    int position,
+                                    const char *other_key,
+                                    int idx,
+                                    enum INI_VA flags);
+
+/**
+ * @brief Function to delete a value.
+ *
+ * Function deletes a value specified either by key or by position.
+ * For example the caller can remove first or last key of the section as well
+ * as first, last or specific instance of a specific key. If key is searched
+ * by index and index is out of range the function would return ENOENT
+ * indicating that specifically requested key is not found.
+ *
+ * @param[in]  ini_config      Configuration object to modify.
+ * @param[in]  section         Name of the section to remove the key from.
+ * @param[in]  position        A constant that specifies the way the
+ *                             key should be removed. For more information
+ *                             see "disposition constants" section of
+ *                             the collection interface, and specifically
+ *                             the "delete" paragraph.
+ * @param[in]  key             Used in conjunction with 'position' constant.
+ *                             Depending on the value of the position
+ *                             argument the key argument can specify
+ *                             the key to delete or the key that the delete
+ *                             operation should be relative to.
+ * @param[in]  idx             Index of duplicates. For example one can specify
+ *                             that he wants to delete the third duplicate
+ *                             of a specific key. If there are less duplicates,
+ *                             i.e. the index is out of range, the function
+ *                             will return ENOENT.
+ *
+ * @return 0                   - Value was removed.
+ * @return ENOENT              - The value not found.
+ */
+int ini_config_delete_value(struct ini_cfgobj *ini_config,
+                            const char *section,
+                            int position,
+                            const char *key,
+                            int idx);
+
+/**
+ * @brief Function to update a comment.
+ *
+ * Function can be used to find and modify a comment for a specific
+ * key in a specific section.
+ *
+ * @param[in]  ini_config      Configuration object to modify.
+ * @param[in]  section         Name of the section to find the key.
+ * @param[in]  key             The key to find.
+ * @param[in]  comments        The array of comments. Can be NULL. If NULL,
+ *                             the existing comment will be removed if any.
+ *                             The comment lines are expected to start with a
+ *                             standard comment symbol. The function does not
+                               check this.
+ *                             Use empty string to express an empty line.
+ * @param[in]  count_comment   Count of comments. If 0 the comment is
+ *                             constructed using array of strings above.
+ *                             It is expected to be NULL terminated.
+ * @param[in]  idx             Index of duplicates. For example one can specify
+ *                             that he wants to find the third duplicate
+ *                             of a specific key. If there are less duplicates,
+ *                             i.e. the index is out of range, the function
+ *                             will return ENOENT.
+ *
+ * @return 0                   - Value was removed.
+ * @return ENOENT              - The value not found.
+ */
+
+int ini_config_update_comment(struct ini_cfgobj *ini_config,
+                              const char *section,
+                              const char *key,
+                              const char *comments[],
+                              size_t count_comment,
+                              int idx);
+/**
+ * @}
+ */
+#endif
diff --git a/ini/ini_configmod_ut.c b/ini/ini_configmod_ut.c
new file mode 100644
index 0000000000000000000000000000000000000000..e78859b4938efc6e66dd4e6410e2622007f9ad23
--- /dev/null
+++ b/ini/ini_configmod_ut.c
@@ -0,0 +1,1152 @@
+/*
+    INI LIBRARY
+
+    Unit test for the configuration object modification API.
+
+    Copyright (C) Dmitri Pal <dpal@redhat.com> 2014
+
+    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 "config.h"
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+/* #define TRACE_LEVEL 7 */
+#define TRACE_HOME
+#include "trace.h"
+#include "ini_configobj.h"
+#include "ini_config_priv.h"
+#include "collection_tools.h"
+#include "ini_configmod.h"
+#include "path_utils.h"
+
+int verbose = 0;
+
+#define WRAP_SIZE 80
+
+#define INIOUT(foo) \
+    do { \
+        if (verbose) { printf("%30s(%4d): ",__FUNCTION__,__LINE__); foo; } \
+    } while(0)
+
+typedef int (*test_fn)(void);
+
+static void print_configuration(struct ini_cfgobj *in_cfg,
+                         FILE *file)
+{
+    int error;
+    struct simplebuffer *sbobj = NULL;
+    uint32_t left = 0;
+
+    error = simplebuffer_alloc(&sbobj);
+    if (error) {
+        printf("Failed to allocate buffer. Error %d.\n", error);
+        return;
+    }
+
+    error = ini_config_serialize(in_cfg, sbobj);
+    if (error) {
+        printf("Failed to serialize. Error %d.\n", error);
+        simplebuffer_free(sbobj);
+        return;
+    }
+
+    /* Save */
+    left = simplebuffer_get_len(sbobj);
+    while (left > 0) {
+        error = simplebuffer_write(fileno(file), sbobj, &left);
+        if (error) {
+            printf("Failed to write back the configuration %d.\n", error);
+            simplebuffer_free(sbobj);
+            return;
+        }
+    }
+
+    simplebuffer_free(sbobj);
+    return;
+}
+
+
+
+/* Basic test */
+static int basic_test(void)
+{
+    int error = EOK;
+    char indir[PATH_MAX];
+    char srcname[PATH_MAX];
+    char resname[PATH_MAX];
+    char command[PATH_MAX * 3];
+    char *builddir = NULL;
+    char *srcdir = NULL;
+    struct ini_cfgobj *in_cfg = NULL;
+    char bin1[] = { 1, 2, 3 };
+    char bin2[] = { 10, 11, 12 };
+    FILE *file = NULL;
+    const char *comment1[] = { "// This is a real file with some comments",
+                               "" };
+    const char *comment2[] = { "",
+                               "/**/" };
+    const char *comment3[] = { "",
+                               "/* Service section defines",
+                               " * which service are allowed.",
+                               " */           "
+                             };
+    const char *sec_com[] = { "" };
+    const char *str_arr[] = { "dp", "nss", "pam", "info" };
+
+    INIOUT(printf("<==== Start ====>\n"));
+
+    srcdir = getenv("srcdir");
+    builddir = getenv("builddir");
+
+    snprintf(indir, PATH_MAX, "%s/ini/ini.d",
+                    (srcdir == NULL) ? "." : srcdir);
+
+    snprintf(srcname, PATH_MAX, "%s/ini/ini.d/real.conf",
+                    (srcdir == NULL) ? "." : srcdir);
+
+    snprintf(resname, PATH_MAX, "%s/real.conf.manual",
+                    (builddir == NULL) ? "." : builddir);
+
+    /* Create config collection */
+    error = ini_config_create(&in_cfg);
+    if (error) {
+        INIOUT(printf("Failed to create collection. Error %d.\n", error));
+        return error;
+    }
+
+    if ((error = ini_config_add_section(
+                 in_cfg,
+                 "config",
+                 comment1,
+                 2,
+                 COL_DSP_END,
+                 NULL,
+                 0)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "config",
+                 "version",
+                 "0.1",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_section(
+                 in_cfg,
+                 "monitor",
+                 comment2,
+                 2,
+                 COL_DSP_END,
+                 NULL,
+                 0)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "monitor",
+                 "description",
+                 "Monitor Configuration",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_int_value(
+                 in_cfg,
+                 "monitor",
+                 "sbusTimeout",
+                 10,
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "monitor",
+                 "sbusAddress",
+                 "unix:path=/var/lib/sss/pipes/private/dbus",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_unsigned_value(
+                 in_cfg,
+                 "monitor",
+                 "servicePingTime",
+                 10,
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "monitor",
+                 "bad_number",
+                 "5a",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_section(
+                 in_cfg,
+                 "services",
+                 comment3,
+                 4,
+                 COL_DSP_END,
+                 NULL,
+                 0)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "services",
+                 "description",
+                 "Local service configuration",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_arr_value(
+                 in_cfg,
+                 "services",
+                 "activeServices",
+                 str_arr,
+                 4,
+                 ',',
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_section(
+                 in_cfg,
+                 "services/dp",
+                 sec_com,
+                 1,
+                 COL_DSP_END,
+                 NULL,
+                 0)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "services/dp",
+                 "description",
+                 "Data Provider Configuration",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "services/dp",
+                 "command",
+                 "/usr/libexec/sssd/sssd_dp",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_section(
+                 in_cfg,
+                 "services/nss",
+                 sec_com,
+                 1,
+                 COL_DSP_END,
+                 NULL,
+                 0)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "services/nss",
+                 "description",
+                 "NSS Responder Configuration",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "services/nss",
+                 "unixSocket",
+                 "/var/lib/sss/pipes/nss",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "services/nss",
+                 "command",
+                 "/usr/libexec/sssd/sssd_nss",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_section(
+                 in_cfg,
+                 "services/pam",
+                 sec_com,
+                 1,
+                 COL_DSP_END,
+                 NULL,
+                 0)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "services/pam",
+                 "description",
+                 "PAM Responder Configuration",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "services/pam",
+                 "unixSocket",
+                 "/var/lib/sss/pipes/pam",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "services/pam",
+                 "command",
+                 "/usr/libexec/sssd/sssd_pam",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_FRONT,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_section(
+                 in_cfg,
+                 "services/info",
+                 sec_com,
+                 1,
+                 COL_DSP_END,
+                 NULL,
+                 0)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "services/info",
+                 "description",
+                 "InfoPipe Configuration",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "services/info",
+                 "command",
+                 "./sbin/sssd_info",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_section(
+                 in_cfg,
+                 "domains",
+                 sec_com,
+                 1,
+                 COL_DSP_END,
+                 NULL,
+                 0)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "domains",
+                 "domainsOrder",
+                 " , LOCAL,          ,  EXAMPLE.COM"
+                 ", ,     SOMEOTHER.COM    ,  ,",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "domains",
+                 "badarray",
+                 "   ,   ,    ,   ,   ,",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "domains",
+                 "somearray",
+                 ",",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "domains",
+                 "someotherarray",
+                 ", ;",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "domains",
+                 "justdelim",
+                 ":;,;",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "domains",
+                 "yetanother",
+                 "",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_section(
+                 in_cfg,
+                 "domains/LOCAL",
+                 sec_com,
+                 1,
+                 COL_DSP_END,
+                 NULL,
+                 0)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "domains/LOCAL",
+                 "description",
+                 "Reserved domain for local configurations",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "domains/LOCAL",
+                 "legacy",
+                 "FALSE",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_long_value(
+                 in_cfg,
+                 "domains/LOCAL",
+                 "enumerate",
+                 3,
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_section(
+                 in_cfg,
+                 "domains/EXAMPLE.COM",
+                 sec_com,
+                 1,
+                 COL_DSP_END,
+                 NULL,
+                 0)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "domains/EXAMPLE.COM",
+                 "description",
+                 "Example domain served by IPA ",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "domains/EXAMPLE.COM",
+                 "provider",
+                 "ipa",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "domains/EXAMPLE.COM",
+                 "server",
+                 "ipaserver1.example.com",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "domains/EXAMPLE.COM",
+                 "server",
+                 "ipabackupserver.example.com",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "domains/EXAMPLE.COM",
+                 "legacy",
+                 "FALSE",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "domains/EXAMPLE.COM",
+                 "server",
+                 "otheripabackupserver.example.com",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_int64_value(
+                 in_cfg,
+                 "domains/EXAMPLE.COM",
+                 "enumerate",
+                 0,
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_bin_value(
+                 in_cfg,
+                 "domains/EXAMPLE.COM",
+                 "binary_test",
+                 bin1,
+                 3,
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_bin_value(
+                 in_cfg,
+                 "domains/EXAMPLE.COM",
+                 "binary_test_two",
+                 bin2,
+                 3,
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "domains/EXAMPLE.COM",
+                 "long_array",
+                 "1  2; 4' ;8p .16/ 32?",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "domains/EXAMPLE.COM",
+                 "double_array",
+                 "1.1  2.222222; .4' . ;8p .16/ -32?",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "domains/EXAMPLE.COM",
+                 "server",
+                 "yetanotheripabackupserver.example.com",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "domains/EXAMPLE.COM",
+                 "empty_value",
+                 "",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "domains/EXAMPLE.COM",
+                 "space_value",
+                 "\" \"",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "domains/EXAMPLE.COM",
+                 "int32_t",
+                 "-1000000000",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "domains/EXAMPLE.COM",
+                 "uint32_t",
+                 "3000000000",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "domains/EXAMPLE.COM",
+                 "int64_t",
+                 "-1000000000123",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "domains/EXAMPLE.COM",
+                 "uint64_t",
+                 "3000000000123",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_END,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK))) {
+        INIOUT(printf("Failed to create configuration. Error %d.\n", error));
+        print_configuration(in_cfg, stdout);
+        ini_config_destroy(in_cfg);
+        return error;
+    }
+
+    file = fopen(resname, "w");
+    print_configuration(in_cfg, file);
+    fclose(file);
+    ini_config_destroy(in_cfg);
+
+    snprintf(command, PATH_MAX * 3, "diff -wi %s %s", srcname, resname);
+    error = system(command);
+    INIOUT(printf("Comparison of %s %s returned: %d\n",
+                  srcname, resname, error));
+
+    if ((error) || (WEXITSTATUS(error))) {
+        printf("Failed to run diff command %d %d.\n",  error,
+               WEXITSTATUS(error));
+        return -1;
+    }
+
+
+    INIOUT(printf("<==== End ====>\n"));
+
+    return EOK;
+}
+
+
+static void make_results(const char *path)
+{
+    FILE *file = NULL;
+
+    file = fopen(path, "w");
+
+    fprintf(file, "// This is a test\n"
+                  "[one]\n"
+                  "key1 = value1\n"
+                  "key1 = value1a\n"
+                  "key1 = value1a_bis\n"
+                  "// This is a test\n"
+                  "key1 = value1b\n"
+                  "key2 = value2\n"
+                  "key3 = value3\n");
+    fclose(file);
+}
+
+
+/* Test duplicates */
+static int dup_test(void)
+{
+    int error = EOK;
+    char srcname[PATH_MAX];
+    char resname[PATH_MAX];
+    char command[PATH_MAX * 3];
+    char *builddir = NULL;
+    struct ini_cfgobj *in_cfg = NULL;
+    FILE *file = NULL;
+    const char *comment[] = { "// This is a test", NULL };
+
+    INIOUT(printf("<==== Start ====>\n"));
+
+    builddir = getenv("builddir");
+
+    snprintf(srcname, PATH_MAX, "%s/modtest.conf.exp",
+                    (builddir == NULL) ? "." : builddir);
+
+    snprintf(resname, PATH_MAX, "%s/modtest.conf.real",
+                    (builddir == NULL) ? "." : builddir);
+
+    make_results(srcname);
+
+    /* Create config collection */
+    error = ini_config_create(&in_cfg);
+    if (error) {
+        INIOUT(printf("Failed to create collection. Error %d.\n", error));
+        return error;
+    }
+
+    if ((error = ini_config_add_section(
+                 in_cfg,
+                 "one",
+                 NULL,
+                 0,
+                 COL_DSP_END,
+                 NULL,
+                 0)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "one",
+                 "key2",
+                 "value2",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_FRONT,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "one",
+                 "key1",
+                 "value1a",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_BEFORE,
+                 "key2",
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "one",
+                 "key3",
+                 "value3",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_AFTER,
+                 "key2",
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "one",
+                 "key1",
+                 "value1",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_FIRSTDUP,
+                 "key1",
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "one",
+                 "key1",
+                 "value1b",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_LASTDUP,
+                 "key1",
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "one",
+                 "key1",
+                 "value1c",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_LASTDUP,
+                 "key1",
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "one",
+                 "key1",
+                 "value1a_bis",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_NDUPNS,
+                 "key1",
+                 2,
+                 INI_VA_NOCHECK))) {
+        INIOUT(printf("Failed to create configuration. Error %d.\n", error));
+        print_configuration(in_cfg, stdout);
+        ini_config_destroy(in_cfg);
+        return error;
+    }
+
+    /* Try to add another section "one" */
+    if (!(error = ini_config_add_section(
+                 in_cfg,
+                 "one",
+                 NULL,
+                 0,
+                 COL_DSP_END,
+                 NULL,
+                 0))) {
+        INIOUT(printf("Expected error. Got %d.\n", error));
+        print_configuration(in_cfg, stdout);
+        ini_config_destroy(in_cfg);
+        return error;
+    }
+
+    if ((error = ini_config_comment_section(in_cfg,
+                                            "one",
+                                            comment,
+                                            1))) {
+        INIOUT(printf("Failed to add a comment %d.\n", error));
+        print_configuration(in_cfg, stdout);
+        ini_config_destroy(in_cfg);
+        return error;
+    }
+
+    if ((error = ini_config_add_section(
+                 in_cfg,
+                 "two",
+                 NULL,
+                 0,
+                 COL_DSP_FRONT,
+                 NULL,
+                 0)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "two",
+                 "key2",
+                 "value2",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_FRONT,
+                 NULL,
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "two",
+                 "key1",
+                 "value1a",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_BEFORE,
+                 "key2",
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "two",
+                 "key3",
+                 "value3",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_AFTER,
+                 "key2",
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "two",
+                 "key1",
+                 "value1",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_FIRSTDUP,
+                 "key1",
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "two",
+                 "key1",
+                 "value1b",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_LASTDUP,
+                 "key1",
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "two",
+                 "key1",
+                 "value1c",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_LASTDUP,
+                 "key1",
+                 0,
+                 INI_VA_NOCHECK)) ||
+        (error = ini_config_add_str_value(
+                 in_cfg,
+                 "two",
+                 "key1",
+                 "value1a_bis",
+                 NULL,
+                 0,
+                 WRAP_SIZE,
+                 COL_DSP_NDUPNS,
+                 "key1",
+                 2,
+                 INI_VA_NOCHECK))) {
+        INIOUT(printf("Failed to add another section. Error %d.\n", error));
+        print_configuration(in_cfg, stdout);
+        ini_config_destroy(in_cfg);
+        return error;
+    }
+
+    /* Rename section */
+    if ((error = ini_config_rename_section(in_cfg,
+                                           "two",
+                                           "three"))) {
+        INIOUT(printf("Failed to rename a section %d.\n", error));
+        print_configuration(in_cfg, stdout);
+        ini_config_destroy(in_cfg);
+        return error;
+    }
+
+    /* Delect section that is before section "one" */
+    if ((error = ini_config_delete_section_by_position(in_cfg,
+                                                       COL_DSP_BEFORE,
+                                                       "one",
+                                                       0))) {
+        INIOUT(printf("Failed to delete a section %d.\n", error));
+        print_configuration(in_cfg, stdout);
+        ini_config_destroy(in_cfg);
+        return error;
+    }
+
+    /* Update comment */
+    if ((error = ini_config_update_comment(in_cfg,
+                                           "one",
+                                           "key1",
+                                            comment,
+                                            1,
+                                            3))) {
+        INIOUT(printf("Failed to update comment %d.\n", error));
+        print_configuration(in_cfg, stdout);
+        ini_config_destroy(in_cfg);
+        return error;
+    }
+
+    /* Delete the key */
+    if ((error = ini_config_delete_value(in_cfg,
+                                         "one",
+                                         COL_DSP_NDUP,
+                                         "key1",
+                                         4))) {
+        INIOUT(printf("Failed to delete the key %d.\n", error));
+        print_configuration(in_cfg, stdout);
+        ini_config_destroy(in_cfg);
+        return error;
+    }
+
+    file = fopen(resname, "w");
+    print_configuration(in_cfg, file);
+    fclose(file);
+    ini_config_destroy(in_cfg);
+
+    snprintf(command, PATH_MAX * 3, "diff -wi %s %s", srcname, resname);
+    error = system(command);
+    INIOUT(printf("Comparison of %s %s returned: %d\n",
+                  srcname, resname, error));
+
+    if ((error) || (WEXITSTATUS(error))) {
+        printf("Failed to run diff command %d %d.\n",  error,
+               WEXITSTATUS(error));
+        return -1;
+    }
+
+
+    INIOUT(printf("<==== End ====>\n"));
+
+    return EOK;
+}
+
+int main(int argc, char *argv[])
+{
+    int error = EOK;
+    test_fn tests[] = { basic_test,
+                        dup_test,
+                        NULL };
+    test_fn t;
+    int i = 0;
+    char *var;
+
+    if ((argc > 1) && (strcmp(argv[1], "-v") == 0)) verbose = 1;
+    else {
+        var = getenv("COMMON_TEST_VERBOSE");
+        if (var) verbose = 1;
+    }
+
+    INIOUT(printf("Start\n"));
+
+    while ((t = tests[i++])) {
+        error = t();
+        if (error) {
+            printf("Failed with error %d!\n", error);
+            return error;
+        }
+    }
+
+    INIOUT(printf("Success!\n"));
+    return 0;
+}
-- 
1.7.1

