From 298e7e580342e0460bd4f4d04d3bd666587126a2 Mon Sep 17 00:00:00 2001
From: Dmitri Pal <dpal@redhat.com>
Date: Thu, 10 Jun 2010 11:57:25 -0400
Subject: [PATCH] [INI] Introducing Value object

Value object is an object that combines
a potentially multiline value
comment and some statistical information
regarding a configuration value.

Patch includes:
 Source
 Header
 Unit test
 Makefile changes
---
 common/ini/Makefile.am       |   22 +-
 common/ini/ini_valueobj.c    |  901 ++++++++++++++++++++++++++++++++++++++++++
 common/ini/ini_valueobj.h    |  130 ++++++
 common/ini/ini_valueobj_ut.c |  538 +++++++++++++++++++++++++
 4 files changed, 1585 insertions(+), 6 deletions(-)
 create mode 100644 common/ini/ini_valueobj.c
 create mode 100644 common/ini/ini_valueobj.h
 create mode 100644 common/ini/ini_valueobj_ut.c

diff --git a/common/ini/Makefile.am b/common/ini/Makefile.am
index ee6c6dee07104e8960f36b502ff0b74394375421..eca3bb0d5c34b941d8ccf092cf091e6cb71047c6 100644
--- a/common/ini/Makefile.am
+++ b/common/ini/Makefile.am
@@ -14,7 +14,8 @@ if HAVE_GCC
 endif
 
 AM_CPPFLAGS = -I$(topdir) -I$(topdir)/trace -I$(topdir)/collection \
-              -I$(topdir)/path_utils -I$(topdir)/refarray $(TRACE_LEVEL)
+              -I$(topdir)/path_utils -I$(topdir)/refarray \
+              -I$(topdir)/basicobjects $(TRACE_LEVEL)
 
 ACLOCAL_AMFLAGS = -I m4
 
@@ -46,13 +47,16 @@ libini_config_la_SOURCES = \
     ini_metadata.h \
     ini_defines.h \
     ini_comment.c \
-    ini_comment.h
+    ini_comment.h \
+    ini_valueobj.c \
+    ini_valueobj.h
 
 
 libini_config_la_LIBADD = \
     -L$(topbuilddir)/collection \
     -L$(topbuilddir)/path_utils \
     -L$(topbuilddir)/refarray \
+    -L$(topbuilddir)/basicobjects \
     -lcollection \
     -lref_array \
     -lpath_utils
@@ -60,11 +64,17 @@ libini_config_la_LDFLAGS = \
     -version-info 2:0:0
 
 # Build unit test
-check_PROGRAMS = ini_config_ut ini_comment_ut
+check_PROGRAMS = ini_config_ut ini_comment_ut ini_valueobj_ut
 ini_config_ut_SOURCES = ini_config_ut.c
-ini_config_ut_LDADD = libini_config.la -L$(topbuilddir)/collection -lcollection
+ini_config_ut_LDADD = libini_config.la \
+                      -L$(topbuilddir)/collection -lcollection \
+                      -L$(topbuilddir)/basicobjects -lbasicobjects
 ini_comment_ut_SOURCES = ini_comment_ut.c
-ini_comment_ut_LDADD = libini_config.la
+ini_comment_ut_LDADD = libini_config.la \
+                        -L$(topbuilddir)/basicobjects -lbasicobjects
+ini_valueobj_ut_SOURCES = ini_valueobj_ut.c
+ini_valueobj_ut_LDADD = libini_config.la \
+                        -L$(topbuilddir)/basicobjects -lbasicobjects
 
 if HAVE_DOXYGEN
 docs:
@@ -78,7 +88,7 @@ docs:
 	@exit 1
 endif
 
-TESTS = ini_config_ut ini_comment_ut
+TESTS = ini_config_ut ini_comment_ut ini_valueobj_ut
 
 tests: all $(check_PROGRAMS)
 
diff --git a/common/ini/ini_valueobj.c b/common/ini/ini_valueobj.c
new file mode 100644
index 0000000000000000000000000000000000000000..e21e9e6c5037b284dc99a319c823882149fb78c5
--- /dev/null
+++ b/common/ini/ini_valueobj.c
@@ -0,0 +1,901 @@
+/*
+    INI LIBRARY
+
+    Module represents interface to the value object.
+
+    Copyright (C) Dmitri Pal <dpal@redhat.com> 2010
+
+    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
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include "config.h"
+#include "simplebuffer.h"
+#include "ref_array.h"
+#include "ini_comment.h"
+#include "trace.h"
+
+struct value_obj {
+    struct ref_array *raw_lines;
+    struct ref_array *raw_lengths;
+    struct simplebuffer *unfolded;
+    uint32_t origin;
+    uint32_t line;
+    uint32_t keylen;
+    uint32_t boundary;
+    struct ini_comment *ic;
+};
+
+/* Size of the block for a value */
+#define INI_VALUE_BLOCK 100
+
+/* The length of " =" which is 3 */
+#define INI_FOLDING_OVERHEAD 3
+
+/* Array growth */
+#define INI_ARRAY_GROW  2
+
+/* Equal sign */
+#define INI_EQUAL_SIGN  " = "
+
+
+/* Unfold the value represented by the array */
+static int value_unfold(struct ref_array *raw_lines,
+                        struct ref_array *raw_lengths,
+                        struct simplebuffer **unfolded)
+{
+    int error;
+    struct simplebuffer *oneline = NULL;
+    uint32_t len = 0;
+    char *ptr = NULL;
+    uint32_t i = 0;
+    char *part = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    error = simplebuffer_alloc(&oneline);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to allocate dynamic string.", error);
+        return error;
+    }
+
+    for (;;) {
+        /* Get line */
+        ptr = ref_array_get(raw_lines, i, NULL);
+        if (ptr) {
+            /* Get its length */
+            ref_array_get(raw_lengths, i, (void *)&len);
+
+            part = *((char **)(ptr));
+
+            TRACE_INFO_STRING("Value:", part);
+            TRACE_INFO_NUMBER("Lenght:", len);
+
+            error = simplebuffer_add_raw(oneline,
+                                         part,
+                                         len,
+                                         INI_VALUE_BLOCK);
+            if (error) {
+                TRACE_ERROR_NUMBER("Failed to add string", error);
+                simplebuffer_free(oneline);
+                return error;
+            }
+
+            i++;
+        }
+        else break;
+    }
+
+    *unfolded = oneline;
+
+    TRACE_FLOW_EXIT();
+    return error;
+}
+
+
+static int save_portion(struct ref_array *raw_lines,
+                        struct ref_array *raw_lengths,
+                        const char* buf,
+                        uint32_t len)
+{
+    int error = EOK;
+    char *copy = NULL;
+    uint32_t adj = 0;
+
+    TRACE_FLOW_ENTRY();
+
+    /* Add leading space only if there is
+     * a) no space
+     * b) it is not an empty line
+     * c) it is now a first line
+     */
+
+    if ((buf[0] != ' ') &&
+        (buf[0] != '\t') &&
+        (len != 0) &&
+        (ref_array_len(raw_lines) != 0)) adj = 1;
+
+    copy = malloc(len + adj + 1);
+    if (!copy) {
+        TRACE_ERROR_NUMBER("Failed to allocate memory", ENOMEM);
+        return ENOMEM;
+    }
+
+    memcpy(copy + adj, buf, len);
+    len += adj;
+    copy[len] = 0;
+
+    /* If the section being saved is not starting
+     * with space add a space.
+     */
+    if (adj) copy[0] = ' ';
+
+    error = ref_array_append(raw_lines, (void *)(&copy));
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to append line",
+                            error);
+        free(copy);
+        return error;
+    }
+
+    error = ref_array_append(raw_lengths, (void *)(&len));
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to append length",
+                            error);
+        return error;
+    }
+
+    TRACE_INFO_STRING("Added string:", (char *)copy);
+    TRACE_INFO_NUMBER("Added number:", len);
+
+
+    TRACE_FLOW_EXIT();
+    return EOK;
+}
+
+/* Function to create a folded value out of the unfolded string */
+static int value_fold(struct simplebuffer *unfolded,
+                      uint32_t key_len,
+                      uint32_t fold_bound,
+                      struct ref_array *raw_lines,
+                      struct ref_array *raw_lengths)
+{
+    int error;
+    const char *buf;
+    uint32_t len = 0;          /* Full length of the buffer          */
+    uint32_t fold_place = 0;   /* Potential folding place            */
+    uint32_t best_place = 0;   /* Dynamic folding boundary           */
+    uint32_t next_place = 0;   /* Position of the found space        */
+    uint32_t fold_len = 0;     /* Determined length of the substring */
+    uint32_t idx = 0;          /* Counter of lines                   */
+    uint32_t i = 0;            /* Internal counter                   */
+    uint32_t resume_place = 0; /* Place we resume parsing            */
+    uint32_t start_place = 0;  /* Start of the string                */
+    int done = 0;              /* Are we done?                       */
+
+    TRACE_FLOW_ENTRY();
+
+    /* Reset arrays */
+    ref_array_reset(raw_lines);
+    ref_array_reset(raw_lengths);
+
+    /* Get the buffer info */
+    len = simplebuffer_get_len(unfolded);
+    buf = (const char *)simplebuffer_get_buf(unfolded);
+
+    /* Make sure that we have at least one character to fold */
+    if (fold_bound == 0) fold_bound++;
+
+    while (!done) {
+        /* Determine the max length of the line */
+        if (idx == 0) {
+             if (fold_bound > (key_len + INI_FOLDING_OVERHEAD)) {
+                 best_place = fold_bound - key_len - INI_FOLDING_OVERHEAD;
+             }
+             else best_place = 0;
+        }
+        else {
+             best_place = fold_bound;
+        }
+
+        TRACE_INFO_NUMBER("Best place", best_place);
+
+        fold_place = start_place;
+        next_place = start_place;
+        best_place += start_place;
+
+        /* Parse the buffer from the right place */
+        for (i = resume_place; i <= len; i++) {
+
+            /* Check for folding opportunity */
+            if (i == len) {
+                next_place = i;
+                done = 1;
+            }
+            /*
+             * Fold if we found the separator or the first line
+             * is too long right away
+             */
+            else if (((buf[i] == ' ') || (buf[i] == '\t')) ||
+                     ((best_place == 0) && (i == 0))) {
+                next_place = i;
+                TRACE_INFO_NUMBER("Next place:", next_place);
+            }
+            else continue;
+
+            if ((next_place > best_place) || (next_place == 0)) {
+                if ((fold_place == start_place) &&
+                    (next_place != 0)) {
+                    /* Our first found folding place
+                     * is already after the preferred
+                     * folding place. Time to fold then...
+                     */
+                    fold_len = next_place - start_place;
+
+                }
+                else {
+                    /* We will use the previous
+                     * folding place.
+                     */
+                    fold_len = fold_place - start_place;
+
+                }
+
+                TRACE_INFO_NUMBER("Fold len:", fold_len);
+
+                error = save_portion(raw_lines,
+                                     raw_lengths,
+                                     buf + start_place,
+                                     fold_len);
+                if (error) {
+                    TRACE_ERROR_NUMBER("Failed to save", error);
+                    return error;
+                }
+
+                start_place += fold_len;
+                /*
+                 * This will force the re-processing
+                 * of the same space but it is
+                 * helpful in case the middle portion
+                 * of the value is beyond our folding limit.
+                 */
+                resume_place = next_place;
+                idx++;
+                break;
+            }
+            else { /* Case when next_place <= best_place */
+                fold_place = next_place;
+            }
+        }
+
+        /* Save last portion */
+        if (done) {
+            error = save_portion(raw_lines,
+                                 raw_lengths,
+                                 buf + start_place,
+                                 next_place - start_place);
+            if (error) {
+                TRACE_ERROR_NUMBER("Failed to save last chunk", error);
+                return error;
+            }
+            idx++;
+        }
+    }
+
+    TRACE_FLOW_EXIT();
+    return error;
+}
+
+
+/* Create value from a referenced array */
+int value_create_from_refarray(struct ref_array *raw_lines,
+                               struct ref_array *raw_lengths,
+                               uint32_t line,
+                               uint32_t origin,
+                               uint32_t key_len,
+                               uint32_t boundary,
+                               struct ini_comment *ic,
+                               struct value_obj **vo)
+{
+    int error = EOK;
+    struct value_obj *new_vo = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    if ((!raw_lines) || (!raw_lengths) || (!vo)) {
+        TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
+        return EINVAL;
+
+    }
+
+    new_vo = malloc(sizeof(struct value_obj));
+    if (!new_vo) {
+        TRACE_ERROR_NUMBER("No memory", ENOMEM);
+        return ENOMEM;
+
+    }
+
+    /* We are not using references here since
+     * it will be inconsistent with the way
+     * how comment is handled.
+     * We could have added references here and make
+     * comment keep references but it seems to be
+     * and overhead in this case.
+     */
+    new_vo->raw_lines = raw_lines;
+    new_vo->raw_lengths = raw_lengths;
+    new_vo->origin = origin;
+    new_vo->line = line;
+    new_vo->keylen = key_len;
+    new_vo->boundary = boundary;
+    new_vo->ic = ic;
+
+    error = value_unfold(new_vo->raw_lines,
+                         new_vo->raw_lengths,
+                         &(new_vo->unfolded));
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to unfold", error);
+        return error;
+    }
+
+    TRACE_INFO_STRING("Unfolded:",
+                      (const char *)simplebuffer_get_buf(new_vo->unfolded));
+    *vo = new_vo;
+
+    TRACE_FLOW_EXIT();
+
+    return error;
+}
+
+/* Cleanup callback for lines array */
+void value_lines_cleanup_cb(void *elem,
+                            ref_array_del_enum type,
+                            void *data)
+{
+    char *part;
+
+    TRACE_FLOW_ENTRY();
+
+    part = *((char **)(elem));
+
+    TRACE_INFO_STRING("Freeing:", part);
+
+    free(part);
+
+    TRACE_FLOW_EXIT();
+}
+
+/* Create a pair of arrays */
+int value_create_arrays(struct ref_array **raw_lines,
+                        struct ref_array **raw_lengths)
+{
+    int error = EOK;
+    struct ref_array *new_lines = NULL;
+    struct ref_array *new_lengths = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    error = ref_array_create(&new_lines,
+                             sizeof(char *),
+                             INI_ARRAY_GROW,
+                             value_lines_cleanup_cb,
+                             NULL);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to create lines array", error);
+        return error;
+
+    }
+
+    error = ref_array_create(&new_lengths,
+                             sizeof(uint32_t),
+                             INI_ARRAY_GROW,
+                             NULL,
+                             NULL);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to create lengths array", error);
+        ref_array_destroy(new_lines);
+        return error;
+
+    }
+
+    *raw_lines = new_lines;
+    *raw_lengths = new_lengths;
+
+    TRACE_FLOW_EXIT();
+    return EOK;
+}
+
+/* Add a raw string to the arrays */
+int value_add_to_arrays(const char *strvalue,
+                        uint32_t len,
+                        struct ref_array *raw_lines,
+                        struct ref_array *raw_lengths)
+{
+    int error = EOK;
+
+    TRACE_FLOW_ENTRY();
+
+    error = ref_array_append(raw_lines, (void *)(&strvalue));
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to add to lines array", error);
+        return error;
+
+    }
+
+    error = ref_array_append(raw_lengths, (void *)(&len));
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to add to lengths array", error);
+        return error;
+
+    }
+
+    TRACE_FLOW_EXIT();
+    return error;
+}
+
+
+/* Destroy arrays */
+void value_destroy_arrays(struct ref_array *raw_lines,
+                          struct ref_array *raw_lengths)
+{
+    TRACE_FLOW_ENTRY();
+
+    /* Function checks validity inside */
+    ref_array_destroy(raw_lines);
+    /* Function checks validity inside */
+    ref_array_destroy(raw_lengths);
+
+    TRACE_FLOW_EXIT();
+
+}
+
+/* Destroy a value object */
+void value_destroy(struct value_obj *vo)
+{
+    TRACE_FLOW_ENTRY();
+
+    if (vo) {
+        /* Free arrays if any */
+        value_destroy_arrays(vo->raw_lines,
+                             vo->raw_lengths);
+        /* Free the simple buffer if any */
+        simplebuffer_free(vo->unfolded);
+        /* Function checks validity inside */
+        ini_comment_destroy(vo->ic);
+        free(vo);
+    }
+
+    TRACE_FLOW_EXIT();
+}
+
+/* Create value object from string buffer */
+int value_create_new(const char *strvalue,
+                     uint32_t length,
+                     uint32_t origin,
+                     uint32_t key_len,
+                     uint32_t boundary,
+                     struct ini_comment *ic,
+                     struct value_obj **vo)
+{
+    int error = EOK;
+    struct value_obj *new_vo = NULL;
+    struct simplebuffer *oneline = NULL;
+    void *val;
+
+    TRACE_FLOW_ENTRY();
+
+    if ((!strvalue) || (!vo)) {
+        TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
+        return EINVAL;
+    }
+
+    /* Create buffer to hold the value */
+    error = simplebuffer_alloc(&oneline);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to allocate dynamic string.", error);
+        return error;
+    }
+
+    /* Deal with the type casting */
+    memcpy(&val, &strvalue, sizeof(char *));
+    /* Put value into the buffer */
+    error = simplebuffer_add_raw(oneline,
+                                 val,
+                                 length,
+                                 INI_VALUE_BLOCK);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to add string", error);
+        simplebuffer_free(oneline);
+        return error;
+    }
+
+    /* Acllocate new INI value structure */
+    new_vo = malloc(sizeof(struct value_obj));
+    if (!new_vo) {
+        TRACE_ERROR_NUMBER("No memory", ENOMEM);
+        simplebuffer_free(oneline);
+        return ENOMEM;
+    }
+
+    new_vo->origin = origin;
+    /* Line is not known in this case */
+    new_vo->line = 0;
+    new_vo->ic = ic;
+    new_vo->unfolded = oneline;
+    new_vo->keylen = key_len;
+    new_vo->boundary = boundary;
+    new_vo->raw_lines = NULL;
+    new_vo->raw_lengths = NULL;
+
+    error = value_create_arrays(&(new_vo->raw_lines),
+                                &(new_vo->raw_lengths));
+
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to fold", error);
+        value_destroy(new_vo);
+        return error;
+    }
+
+    /* Create arrays by folding the value */
+    error = value_fold(new_vo->unfolded,
+                       new_vo->keylen,
+                       new_vo->boundary,
+                       new_vo->raw_lines,
+                       new_vo->raw_lengths);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to fold", error);
+        value_destroy(new_vo);
+        return error;
+    }
+
+    *vo = new_vo;
+
+    TRACE_FLOW_EXIT();
+
+    return error;
+}
+
+/* Get concatenated value */
+int value_get_concatenated(struct value_obj *vo,
+                           const char **fullstr)
+{
+    TRACE_FLOW_ENTRY();
+
+    if (!vo) {
+        TRACE_ERROR_NUMBER("Invalid object", EINVAL);
+        return EINVAL;
+    }
+
+    *fullstr = (const char *)simplebuffer_get_buf(vo->unfolded);
+
+    TRACE_FLOW_EXIT();
+    return EOK;
+}
+
+/* Get value's origin */
+int value_get_origin(struct value_obj *vo, uint32_t *origin)
+{
+    TRACE_FLOW_ENTRY();
+
+    if (!vo) {
+        TRACE_ERROR_NUMBER("Invalid object", EINVAL);
+        return EINVAL;
+    }
+
+    *origin = vo->origin;
+
+    TRACE_FLOW_EXIT();
+    return EOK;
+}
+
+/* Get value's line */
+int value_get_line(struct value_obj *vo, uint32_t *line)
+{
+    TRACE_FLOW_ENTRY();
+
+    if (!vo) {
+        TRACE_ERROR_NUMBER("Invalid object", EINVAL);
+        return EINVAL;
+    }
+
+    *line = vo->line;
+
+    TRACE_FLOW_EXIT();
+    return EOK;
+}
+
+/* Update key length */
+int value_set_keylen(struct value_obj *vo, uint32_t key_len)
+{
+    int error = EOK;
+    TRACE_FLOW_ENTRY();
+
+    if (!vo) {
+        TRACE_ERROR_NUMBER("Invalid object", EINVAL);
+        return EINVAL;
+    }
+
+    vo->keylen = key_len;
+
+    /* Fold in new value */
+    error = value_fold(vo->unfolded,
+                       vo->keylen,
+                       vo->boundary,
+                       vo->raw_lines,
+                       vo->raw_lengths);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to fold", error);
+        /* In this case nothing to free here but
+         * the object might be unsiable */
+        return error;
+    }
+
+    TRACE_FLOW_EXIT();
+    return EOK;
+}
+
+
+/* Update value */
+int value_update(struct value_obj *vo,
+                 const char *value,
+                 uint32_t length,
+                 uint32_t origin,
+                 uint32_t boundary)
+{
+    int error = EOK;
+    struct simplebuffer *oneline = NULL;
+    void *val;
+
+    if ((!value) || (!vo)) {
+        TRACE_ERROR_NUMBER("Invalid argument", EINVAL);
+        return EINVAL;
+    }
+
+    /* Create buffer to hold the value */
+    error = simplebuffer_alloc(&oneline);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to allocate dynamic string.", error);
+        return error;
+    }
+
+    /* Deal with the type cast */
+    memcpy(&val, &value, sizeof(char *));
+    /* Put value into the buffer */
+    error = simplebuffer_add_raw(oneline,
+                                 val,
+                                 length,
+                                 INI_VALUE_BLOCK);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to add string", error);
+        simplebuffer_free(oneline);
+        return error;
+    }
+
+    simplebuffer_free(vo->unfolded);
+
+    vo->origin = origin;
+    vo->unfolded = oneline;
+    vo->boundary = boundary;
+
+    /* Fold in new value */
+    error = value_fold(vo->unfolded,
+                       vo->keylen,
+                       vo->boundary,
+                       vo->raw_lines,
+                       vo->raw_lengths);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to fold", error);
+        /* In this case nothing to free here but
+         * the object might be unsiable */
+        return error;
+    }
+
+    TRACE_FLOW_EXIT();
+
+    return error;
+
+}
+
+/* Get comment from the value */
+int value_extract_comment(struct value_obj *vo, struct ini_comment **ic)
+{
+    int error = EOK;
+
+    TRACE_FLOW_ENTRY();
+
+    if ((!vo) || (!ic)) {
+        TRACE_ERROR_NUMBER("Invalid input parameter", EINVAL);
+        return EINVAL;
+    }
+
+    *ic = vo->ic;
+    vo->ic = NULL;
+
+    TRACE_FLOW_EXIT();
+    return error;
+
+}
+
+/* Set comment into the value */
+int value_put_comment(struct value_obj *vo, struct ini_comment *ic)
+{
+    int error = EOK;
+
+    TRACE_FLOW_ENTRY();
+
+    if ((!vo) || (!ic)) {
+        TRACE_ERROR_NUMBER("Invalid input parameter", EINVAL);
+        return EINVAL;
+    }
+
+    if (vo->ic != ic) {
+        /* Remove existing comment if any */
+        ini_comment_destroy(vo->ic);
+    }
+
+    vo->ic = ic;
+
+    TRACE_FLOW_EXIT();
+    return error;
+
+}
+
+/* Serialize value */
+int value_serialize(struct value_obj *vo,
+                    const char *key,
+                    struct simplebuffer **sbobj)
+{
+    int error = EOK;
+    uint32_t num = 0;
+    uint32_t i = 0;
+    uint32_t len = 0;
+    char *commentline = NULL;
+    char *ptr = NULL;
+    struct simplebuffer *new_sbobj = NULL;
+    void *val = NULL;
+    const char *eq = INI_EQUAL_SIGN;
+    char *part = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    if (!vo) {
+        TRACE_ERROR_NUMBER("Invalid input parameter", EINVAL);
+        return EINVAL;
+    }
+
+    error = simplebuffer_alloc(&new_sbobj);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to allocate dynamic string.", error);
+        return error;
+    }
+
+    /* Put comment first */
+    if (vo->ic) {
+
+        /* Get number of lines in the comment */
+        error = ini_comment_get_numlines(vo->ic, &num);
+        if (error) {
+            TRACE_ERROR_NUMBER("Failed to get number of lines", errno);
+            simplebuffer_free(new_sbobj);
+            return error;
+        }
+
+        for (i = 0; i < num; i++) {
+
+            len = 0;
+            commentline = NULL;
+
+            error = ini_comment_get_line(vo->ic, i, &commentline, &len);
+            if (error) {
+                TRACE_ERROR_NUMBER("Failed to get number of lines", errno);
+                simplebuffer_free(new_sbobj);
+                return error;
+            }
+
+            error = simplebuffer_add_raw(new_sbobj,
+                                         commentline,
+                                         len,
+                                         INI_VALUE_BLOCK);
+            if (error) {
+                TRACE_ERROR_NUMBER("Failed to add comment", error);
+                simplebuffer_free(new_sbobj);
+                return error;
+            }
+
+            error = simplebuffer_add_cr(new_sbobj);
+            if (error) {
+                TRACE_ERROR_NUMBER("Failed to add CR", error);
+                simplebuffer_free(new_sbobj);
+                return error;
+            }
+        }
+    }
+
+    /* Deal with the type cast */
+    memcpy(&val, &key, sizeof(char *));
+
+    error = simplebuffer_add_raw(new_sbobj,
+                                 val,
+                                 vo->keylen,
+                                 INI_VALUE_BLOCK);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to add key", error);
+        simplebuffer_free(new_sbobj);
+        return error;
+    }
+
+    /* Deal with the type cast */
+    memcpy(&val, &eq, sizeof(char *));
+
+    error = simplebuffer_add_raw(new_sbobj,
+                                 val,
+                                 sizeof(INI_EQUAL_SIGN) - 1,
+                                 INI_VALUE_BLOCK);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to add equal sign", error);
+        simplebuffer_free(new_sbobj);
+        return error;
+    }
+
+
+    if (vo->raw_lines) {
+        i = 0;
+        for (;;) {
+            /* Get line */
+            ptr = ref_array_get(vo->raw_lines, i, NULL);
+            if (ptr) {
+                /* Get its length */
+                ref_array_get(vo->raw_lengths, i, (void *)&len);
+                if (error) {
+                    TRACE_ERROR_NUMBER("Failed to add string", error);
+                    simplebuffer_free(new_sbobj);
+                    return error;
+                }
+
+                part = *((char **)(ptr));
+
+                TRACE_INFO_STRING("Value:", part);
+                TRACE_INFO_NUMBER("Lenght:", len);
+
+                error = simplebuffer_add_raw(new_sbobj,
+                                             part,
+                                             len,
+                                             INI_VALUE_BLOCK);
+                if (error) {
+                    TRACE_ERROR_NUMBER("Failed to add value", error);
+                    simplebuffer_free(new_sbobj);
+                    return error;
+                }
+
+                error = simplebuffer_add_cr(new_sbobj);
+                if (error) {
+                    TRACE_ERROR_NUMBER("Failed to add CR", error);
+                    simplebuffer_free(new_sbobj);
+                    return error;
+                }
+               i++;
+            }
+            else break;
+        }
+    }
+
+    *sbobj = new_sbobj;
+
+    TRACE_INFO_STRING("Buffer:", (const char *)simplebuffer_get_buf(*sbobj));
+    TRACE_FLOW_EXIT();
+    return error;
+}
diff --git a/common/ini/ini_valueobj.h b/common/ini/ini_valueobj.h
new file mode 100644
index 0000000000000000000000000000000000000000..d8e10deb3d1996612dbdbe6fad4b71e84d59736b
--- /dev/null
+++ b/common/ini/ini_valueobj.h
@@ -0,0 +1,130 @@
+/*
+    INI LIBRARY
+
+    Header file for the value object.
+
+    Copyright (C) Dmitri Pal <dpal@redhat.com> 2010
+
+    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_VALUEOBJ_H
+#define INI_VALUEOBJ_H
+
+#include "ref_array.h"
+#include "simplebuffer.h"
+#include "ini_comment.h"
+
+struct value_obj;
+
+#define INI_VALUE_READ      0 /* Value is read from the file */
+#define INI_VALUE_CREATED   1 /* Value is created in memory  */
+
+/*
+ * Create value from a referenced array.
+ *
+ * NOTE: arrays and comment are NOT treated as
+ * objects that keep reference count.
+ * They are created externally and passed in
+ * as separate parts that are glued together
+ * by the value object.
+ * The caller should not free it himself
+ * (only in case of failure) since
+ * after the call the arrays and comment
+ * are owned by the value object and will
+ * be freed when it is destroyed.
+ */
+int value_create_from_refarray(struct ref_array *raw_lines,
+                               struct ref_array *raw_lengths,
+                               uint32_t line,
+                               uint32_t origin,
+                               uint32_t key_len,
+                               uint32_t boundary,
+                               struct ini_comment *ic,
+                               struct value_obj **vo);
+
+/* Cleanup callback for lines array */
+void value_lines_cleanup_cb(void *elem,
+                            ref_array_del_enum type,
+                            void *data);
+
+/* Create a pair of arrays */
+int value_create_arrays(struct ref_array **raw_lines,
+                        struct ref_array **raw_lengths);
+
+/* Add a raw read line to the arrays */
+int value_add_to_arrays(const char *strvalue,
+                        uint32_t len,
+                        struct ref_array *raw_lines,
+                        struct ref_array *raw_lengths);
+
+/* Create a pair of arrays */
+void value_destroy_arrays(struct ref_array *raw_lines,
+                          struct ref_array *raw_lengths);
+
+/* Create value object from string buffer.
+ * NOTE: see note above
+ * in the description of the
+ * value_create_from_refarray function.
+ */
+int value_create_new(const char *strvalue,
+                     uint32_t length,
+                     uint32_t origin,
+                     uint32_t key_len,
+                     uint32_t boundary,
+                     struct ini_comment *ic,
+                     struct value_obj **vo);
+
+/* Destroy a value object */
+void value_destroy(struct value_obj *vo);
+
+/* Get concatenated value */
+int value_get_concatenated(struct value_obj *vo,
+                           const char **fullstr);
+
+/* Get value's origin */
+int value_get_origin(struct value_obj *vo,
+                     uint32_t *origin);
+
+/* Get value's line */
+int value_get_line(struct value_obj *vo,
+                   uint32_t *line);
+
+/* Update key length */
+int value_set_keylen(struct value_obj *vo,
+                     uint32_t key_len);
+
+/* Update value */
+int value_update(struct value_obj *vo,
+                 const char *value,
+                 uint32_t length,
+                 uint32_t origin,
+                 uint32_t boundary);
+
+/* Get comment from the value */
+int value_extract_comment(struct value_obj *vo,
+                          struct ini_comment **ic);
+
+/* Set comment into the value */
+int value_put_comment(struct value_obj *vo,
+                      struct ini_comment *ic);
+
+/* Serialize value */
+int value_serialize(struct value_obj *vo,
+                    const char *key,
+                    struct simplebuffer **sbobj);
+
+
+#endif
diff --git a/common/ini/ini_valueobj_ut.c b/common/ini/ini_valueobj_ut.c
new file mode 100644
index 0000000000000000000000000000000000000000..7736663fe01adbf26d81dc7ce7ad91534b074f57
--- /dev/null
+++ b/common/ini/ini_valueobj_ut.c
@@ -0,0 +1,538 @@
+/*
+    INI LIBRARY
+
+    Unit test for the value object.
+
+    Copyright (C) Dmitri Pal <dpal@redhat.com> 2010
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+    This program 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 General Public License for more details.
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#define _GNU_SOURCE
+#include <errno.h>  /* for errors */
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "ini_valueobj.h"
+#include "ini_defines.h"
+#include "config.h"
+#define TRACE_HOME
+#include "trace.h"
+
+int verbose = 0;
+
+#define VOOUT(foo) \
+    do { \
+        if (verbose) foo; \
+    } while(0)
+
+
+typedef int (*test_fn)(void);
+
+
+extern void ref_array_debug(struct ref_array *ra);
+
+static int create_comment(int i, struct ini_comment **ic)
+{
+    int error = EOK;
+    const char *template = ";Line 0 of the value %d";
+    char comment[80];
+    struct ini_comment *new_ic = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    sprintf(comment, template, i);
+
+
+    if ((error = ini_comment_create(&new_ic)) ||
+        (error = ini_comment_build(new_ic, comment)) ||
+        (error = ini_comment_build(new_ic, NULL)) ||
+        (error = ini_comment_build(new_ic, "#This is the second line")) ||
+        (error = ini_comment_build(new_ic, ";This is the third line")) ||
+        (error = ini_comment_build(new_ic, ""))) {
+        printf("Failed to create comment object\n");
+        ini_comment_destroy(new_ic);
+        return -1;
+    }
+
+    *ic = new_ic;
+
+    TRACE_FLOW_EXIT();
+    return EOK;
+}
+
+/* Save value to the file */
+/* NOTE: might be moved into the API in future */
+int save_value(FILE *ff, const char *key, struct value_obj *vo)
+{
+
+    int error = EOK;
+    struct simplebuffer *sbobj = NULL;
+    uint32_t left = 0;
+
+    TRACE_FLOW_ENTRY();
+
+
+    /* Serialize */
+    error = value_serialize(vo, key, &sbobj);
+    if (error) {
+        printf("Failed to serialize a value object %d.\n", error);
+        return error;
+    }
+
+    /* Add CR */
+    error = simplebuffer_add_cr(sbobj);
+    if (error) {
+        TRACE_ERROR_NUMBER("Failed to add CR", error);
+        simplebuffer_free(sbobj);
+        return error;
+    }
+
+    /* Save */
+    left = simplebuffer_get_len(sbobj);
+    while (left > 0) {
+        error = simplebuffer_write(fileno(ff), sbobj, &left);
+        if (error) {
+            printf("Failed to write value object %d.\n", error);
+            simplebuffer_free(sbobj);
+            return error;
+        }
+    }
+
+    simplebuffer_free(sbobj);
+
+    TRACE_FLOW_EXIT();
+    return EOK;
+}
+
+/* Test to create value object using arrays */
+int other_create_test(FILE *ff, struct value_obj **vo)
+{
+    int error = EOK;
+    struct value_obj *new_vo = NULL;
+    struct ref_array *raw_lines;
+    struct ref_array *raw_lengths;
+    struct ini_comment *ic = NULL;
+    struct ini_comment *ic2 = NULL;
+    char *val;
+    const char *vallines[] = { "Domain1,",
+                               "  Domain2 ,",
+                               "  Domain3" };
+    const char *fullstr;
+    const char *expected = "Domain1,  Domain2 ,  Domain3";
+    int i;
+    uint32_t origin = 0;
+    uint32_t line = 0;
+
+
+    TRACE_FLOW_ENTRY();
+
+    /* Create a pair of arrays */
+    error = value_create_arrays(&raw_lines,
+                                &raw_lengths);
+    if (error) {
+        printf("Failed to create arrays %d.\n", error);
+        return error;
+    }
+
+    for (i=0; i< 3; i++) {
+        errno = 0;
+        val = strdup(vallines[i]);
+        if (val == NULL) {
+            error = errno;
+            printf("Failed to dup memory %d.\n", error);
+            value_destroy_arrays(raw_lines,
+                                raw_lengths);
+            return error;
+        }
+
+        /* Add line to the arrays */
+        error = value_add_to_arrays(val,
+                                    strlen(val),
+                                    raw_lines,
+                                    raw_lengths);
+        if (error) {
+            printf("Failed to add to arrays %d.\n", error);
+            value_destroy_arrays(raw_lines,
+                                raw_lengths);
+            return error;
+        }
+
+    }
+
+    /* Create a comment */
+    error = create_comment(1000, &ic);
+    if (error) {
+        printf("Failed to create comment %d.\n", error);
+        value_destroy_arrays(raw_lines,
+                            raw_lengths);
+        return error;
+    }
+
+    /* Create object */
+    error = value_create_from_refarray(raw_lines,
+                                       raw_lengths,
+                                       1,
+                                       INI_VALUE_READ,
+                                       3,
+                                       70,
+                                       ic,
+                                       &new_vo);
+
+    if (error) {
+        printf("Failed to create comment %d.\n", error);
+        value_destroy_arrays(raw_lines,
+                            raw_lengths);
+        ini_comment_destroy(ic);
+        return error;
+    }
+
+    /* Save value to the file */
+    error = save_value(stdout, "bar", new_vo);
+    if (error) {
+        printf("Failed to save value to file %d.\n", error);
+        value_destroy(new_vo);
+        return error;
+    }
+
+    /* Now do assertions and modifications to the object */
+
+    /* NOTE: Below this line do need to free arrays or comment
+     * they became internal parts of the value object
+     * and will be freed as a part of it.
+     */
+
+    /* Get concatenated value */
+    error = value_get_concatenated(new_vo,
+                                   &fullstr);
+
+    if (error) {
+        printf("Failed to get the string %d.\n", error);
+        value_destroy(new_vo);
+        return error;
+    }
+
+    if (strncmp(fullstr, expected, strlen(expected) + 1) != 0) {
+        printf("The expected value is different.\n%s\n", fullstr);
+        value_destroy(new_vo);
+        return error;
+    }
+
+    /* Get value's origin */
+    error = value_get_origin(new_vo, &origin);
+    if (error) {
+        printf("Failed to get origin %d.\n", error);
+        value_destroy(new_vo);
+        return error;
+    }
+
+    if (origin != INI_VALUE_READ) {
+        printf("The expected origin is different.\n%d\n", origin);
+        value_destroy(new_vo);
+        return error;
+    }
+
+    /* Get value's line */
+    error = value_get_line(new_vo, &line);
+    if (error) {
+        printf("Failed to get origin %d.\n", error);
+        value_destroy(new_vo);
+        return error;
+    }
+
+    if (line != 1) {
+        printf("The expected line is different.\n%d\n", origin);
+        value_destroy(new_vo);
+        return error;
+    }
+
+    /* Get comment from the value */
+    ic = NULL;
+    error = value_extract_comment(new_vo, &ic);
+    if (error) {
+        printf("Failed to extract comment %d.\n", error);
+        value_destroy(new_vo);
+        return error;
+    }
+
+    if (ic == NULL) {
+        printf("The expected comment to be there.\n");
+        value_destroy(new_vo);
+        return error;
+    }
+
+    VOOUT(ini_comment_print(ic, stdout));
+
+    /* Get comment again */
+    ic2 = NULL;
+    error = value_extract_comment(new_vo, &ic2);
+    if (error) {
+        printf("Failed to extract comment %d.\n", error);
+        value_destroy(new_vo);
+        ini_comment_destroy(ic);
+        return error;
+    }
+
+    if (ic2 != NULL) {
+        printf("The expected NO comment to be there.\n");
+        value_destroy(new_vo);
+        ini_comment_destroy(ic);
+        /* No free for ic2 since it is the same object */
+
+        /* But this should not happen anyways -
+         * it will be coding error.
+         */
+        return error;
+    }
+
+    /* Put comment back */
+    error = value_put_comment(new_vo, ic);
+    if (error) {
+        printf("Failed to put comment back %d.\n", error);
+        value_destroy(new_vo);
+        ini_comment_destroy(ic);
+        return error;
+    }
+
+    /* Save value to the file */
+    error = save_value(ff, "bar", new_vo);
+    if (error) {
+        printf("Failed to save value to file %d.\n", error);
+        value_destroy(new_vo);
+        return error;
+    }
+
+    *vo = new_vo;
+
+    TRACE_FLOW_EXIT();
+    return EOK;
+}
+
+/* Modify the value object */
+int modify_test(FILE *ff, struct value_obj *vo)
+{
+    int error = EOK;
+    const char *strval = "Domain100, Domain200, Domain300";
+
+    TRACE_FLOW_ENTRY();
+
+
+    /* Update key length */
+    error = value_set_keylen(vo, strlen("foobar"));
+    if (error) {
+        printf("Failed to change key length %d.\n", error);
+        return error;
+    }
+
+    /* Update value */
+    error = value_update(vo,
+                         strval,
+                         strlen(strval),
+                         INI_VALUE_CREATED,
+                         10);
+
+
+    /* Save value to the file */
+    error = save_value(ff, "foobar", vo);
+    if (error) {
+        printf("Failed to save value to file %d.\n", error);
+        return error;
+    }
+
+    TRACE_FLOW_EXIT();
+    return EOK;
+}
+
+
+int vo_basic_test(void)
+{
+    int error = EOK;
+    const char *strvalue = "Test multi_word_value_that_will_"
+                           "be_split_between_several_lines_!";
+
+    /* Other testing can be done with the following string:
+     * const char *strvalue = "Test multi word value that "
+     *                        "will be split between several lines";
+     */
+
+    struct value_obj *vo = NULL;
+    uint32_t wrap = 0;
+    struct ini_comment *ic = NULL;
+    FILE *ff = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    errno = 0;
+    ff = fopen("test.ini","wt");
+    if (ff == NULL) {
+        error = errno;
+        printf("Failed to open file. Error %d.\n", error);
+        return error;
+    }
+
+
+    for (wrap = 0; wrap < 80; wrap++) {
+
+        ic = NULL;
+        error = create_comment(wrap, &ic);
+        if (error) {
+            printf("Failed to create a new value object %d.\n", error);
+            fclose(ff);
+            return error;
+        }
+
+        error = value_create_new(strvalue,
+                                 strlen(strvalue),
+                                 INI_VALUE_CREATED,
+                                 3,
+                                 wrap,
+                                 ic,
+                                 &vo);
+        if (error) {
+            printf("Failed to create a new value object %d.\n", error);
+            ini_comment_destroy(ic);
+            fclose(ff);
+            return error;
+        }
+
+        error = save_value(ff, "key", vo);
+        if (error) {
+            printf("Failed to save value to file %d.\n", error);
+            value_destroy(vo);
+            return error;
+        }
+
+        value_destroy(vo);
+    }
+
+    /* Run other create test here */
+    error = other_create_test(ff, &vo);
+    if (error) {
+        printf("Create test failed %d.\n", error);
+        fclose(ff);
+        return error;
+    }
+
+    /* Run modify test here */
+    error = modify_test(ff, vo);
+    if (error) {
+        printf("Modify test failed %d.\n", error);
+        fclose(ff);
+        value_destroy(vo);
+        return error;
+    }
+
+    value_destroy(vo);
+
+
+    ic = NULL;
+    error = create_comment(100, &ic);
+    if (error) {
+        printf("Failed to create a new value object %d.\n", error);
+        fclose(ff);
+        return error;
+    }
+
+    ini_comment_print(ic, ff);
+
+    ini_comment_destroy(ic);
+
+    fclose(ff);
+
+    TRACE_FLOW_EXIT();
+    return EOK;
+}
+
+
+int vo_file_test(void)
+{
+    int error = EOK;
+    FILE *ff = NULL;
+    uint32_t line = 0;
+    char *oneline = NULL;
+    char buffer[BUFFER_SIZE + 1];
+
+    TRACE_FLOW_ENTRY();
+
+    errno = 0;
+    ff = fopen("test.ini","r");
+    if (ff == NULL) {
+        error = errno;
+        printf("Failed to open file. Error %d.\n", error);
+        return error;
+    }
+
+    while (1) {
+
+        oneline = NULL;
+        errno = 0;
+        line++;
+
+        oneline = fgets(buffer, BUFFER_SIZE, ff);
+        if (oneline == NULL) {
+            error = errno;
+            if (feof(ff)) break;
+            printf("Failed to read file %d\n", error);
+            fclose(ff);
+            return error;
+        }
+
+        VOOUT(printf("Line %04d: %s", line, oneline));
+
+    }
+
+    fclose(ff);
+
+    /* THIS TEST IS INCOMPLETE.
+     * BUT I NEED TO ADD PARSING
+     * TO COMPLETE IT.
+     * Will be delivered later.
+     */
+
+    TRACE_FLOW_EXIT();
+    return EOK;
+}
+
+
+/* Main function of the unit test */
+int main(int argc, char *argv[])
+{
+    int error = 0;
+    test_fn tests[] = { vo_basic_test,
+                        vo_file_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;
+    }
+
+    VOOUT(printf("Start\n"));
+
+    while ((t = tests[i++])) {
+        error = t();
+        if (error) {
+            VOOUT(printf("Failed with error %d!\n", error));
+            return error;
+        }
+    }
+
+    VOOUT(printf("Success!\n"));
+    return 0;
+}
-- 
1.5.5.6

